Branch sms9110 collapsed to main trunk.
authorshort <>
Sun, 31 Oct 1999 12:44:35 +0000 (12:44 +0000)
committershort <>
Sun, 31 Oct 1999 12:44:35 +0000 (12:44 +0000)
Man page still on fire, has to be merged properly.
Operation modes implemented, --receive added.
Whole code mostly untested.

14 files changed:
AUTHORS
Makefile.am
NEWS
README
acconfig.h
autogen.sh
configure.in
manfmt.c
mdsms.1.c
mdsms.1.in
mdsms.c
need-declaration.m4 [deleted file]
setup.h
strdup.c [deleted file]

diff --git a/AUTHORS b/AUTHORS
index cffe83a..4fc454b 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,5 +1,5 @@
-Author of MobilDock SMS Sender:
--------------------------------
+Author of Mobile Device SMS tool:
+-----------------------------------------------
 
 Name:       Jan Kratochvil
 
index 31b3a78..4dce71f 100644 (file)
@@ -2,13 +2,14 @@
 
 AUTOMAKE_OPTIONS = dist-tarZ
 
-EXTRA_DIST = autogen.sh mdsms.1.in ChangeLog
+EXTRA_DIST = autogen.sh mdsms.1.c mdsms.1.in ChangeLog need-declaration.m4
 
-CLEANFILES = ChangeLog
+CLEANFILES = ChangeLog mdsms.1 mdsms.1.in2
 
 bin_PROGRAMS = mdsms
 man_MANS = mdsms.1
-noinst_PROGRAMS = manfmt mdsms.1
+noinst_PROGRAMS = manfmt
+noinst_SCRIPTS = mdsms.1 mdsms.1.in2
 noinst_HEADERS = setup.h getopt.h
 
 manfmt_SOURCES = manfmt.c
@@ -16,13 +17,16 @@ manfmt_SOURCES = manfmt.c
 mdsms_SOURCES = mdsms.c
 mdsms_LDADD = @LIBOBJS@
 
-mdsms.1.o: mdsms.1.c mdsms.1.in config.h setup.h
+mdsms.1.in2: mdsms.1.in
+       grep -v Id: > mdsms.1.in2 mdsms.1.in
+
+mdsms.1.o: mdsms.1.c mdsms.1.in2 config.h setup.h
        $(CPP) > mdsms.1.o mdsms.1.c
 
 mdsms.1: manfmt mdsms.1.o
        ./manfmt > mdsms.1 < mdsms.1.o
 
-.PHONY: ChangeLog
-
 ChangeLog:
-       rcs2log -u "short       Jan Kratochvil  short@ucw.cz" >ChangeLog
+       rcs2log >ChangeLog
+
+Makefile.in: ChangeLog
diff --git a/NEWS b/NEWS
index 5130256..b3a69f2 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,8 +1,9 @@
-MobilDock SMS sender NEWS
-=========================
+Mobile Device SMS tool NEWS
+===========================
 
 $Id$
 
-1.2: Stabilization release
-  Compatibility problem with Digital UNIX 4.0 termios fixed,
-  all currently known defects have been fixed.
+1.4.1: Fixed, second SMS9110 alpha release
+       Format string fixes, many stupid SIGSEGV caught.
+       SunOS portability fixes.
+       Timeout portability issue resolved (siginterrupt(3)).
diff --git a/README b/README
index d9665d9..2754d15 100644 (file)
--- a/README
+++ b/README
@@ -1,11 +1,20 @@
-MobilDock SMS sender
-====================
+Mobile Device SMS tool
+======================
 
 $Id$
 
+Software homepage:
+------------------
+
+  http://atrey.karlin.mff.cuni.cz/~short/sw/#sms9110
+
+
+Platform compatibility:
+-----------------------
+
   Development platform / device test passed on:
 
-    Linux [i386] (kernel 2.2.7, RedHat based)
+    Linux [i386] (kernel 2.2.10-ac12, RedHat based)
 
   Supported platforms regarding successful compilation. Real functionality
 expected although testing not possible:
@@ -15,14 +24,50 @@ expected although testing not possible:
     Sun SunOS 5.5.1 [sparc/sun4m]
     SGI IRIX 6.5 [mips]
 
+
+Quick installation:
+-------------------
+
   For quick installation type:
 
     ./configure
-               make install
+    make install
 
   For full installation options see file INSTALL.
 
-  Documentation available in 'man' format, type "man mdsms" after
+
+Quick run:
+----------
+
+  Download some logo in NOL/NGG format from KESSLER Wireless Design:
+
+    http://www.kessler-design.com/wireless/samples.php3
+
+  Generally you can send the logo from your phone (marked with 'S' below)
+to any other phone (marked as 'D'). But at least for the first case
+you'll be probably sending the logo to yourself (really possible):
+
+  Port with our 9110:          /dev/ttyS1
+  Baud rate for our 9110:      57600
+S Our phone's SMSC:            +420602909909
+D Destination phone:           +420602431329
+  Logo filename:               /tmp/eurotel.nol
+D Dest. phone network:         23002
+
+  Open your 9110, select System->Fax modem->Settings. Choose cable connection
+with baudrate of 57600. Then Active Fax modem and issue the following UNIX
+command:
+
+./sms9110 -v -d /dev/ttyS1 -b 57600 -s +420602909909 +420602431329 /tmp/eurotel.nol 23002
+
+  It should write some verbose output ending with "SMS sent..." message.
+
+
+Misc:
+-----
+
+  Documentation available in 'man' format, type "man sms9110" after
 installation.
 
-  Contact to author available in file AUTHORS.
+  Contact to author available in file AUTHORS. When submitting bug reports,
+please use double -v -v option.
index c20241f..4859a4b 100644 (file)
 #undef HAVE_CBAUDEX
 #undef CBAUDEX
 
+/* FD_SETSIZE define, may be missing. */
+#undef HAVE_FD_SETSIZE
+
+/* offsetof() define, may be missing. */
+#undef HAVE_OFFSETOF
+
 /* snprintf(3) unsafe emulation */
 #undef HAVE_SNPRINTF
 
 /* vsnprintf(3) unsafe emulation */
 #undef HAVE_VSNPRINTF
 
-/* printf family accepts %m */
-#undef PRINTF_WORKS_PM
-
 /* found lockfile directory */
 #undef DEF_LOCKFILE
 
 /* how to declare __atribute__ ((__unused__)) */
 #undef ATTR_UNUSED
 
+/* whether they have __atttribute__ ((format(printf,...))) */
+#undef HAVE_PRINTFORMAT
+#ifdef HAVE_PRINTFORMAT
+#define ATTR_PRINTFORMAT(a,b) __attribute__ ((format(printf,(a),(b))))
+#else
+#define ATTR_PRINTFORMAT(a,b)
+#endif
+
 @BOTTOM@
+
+#ifndef DONT_NEED_DECLARATIONS
+
 /* Declaration for gethostname(3)? */
 #undef NEED_DECLARATION_GETHOSTNAME
 #ifdef NEED_DECLARATION_GETHOSTNAME
@@ -99,17 +113,75 @@ char *strdup(const char *s);
 void usleep(unsigned long usec);
 #endif
 
+/* Declaration for strcasecmp(3)? */
+#undef NEED_DECLARATION_STRCASECMP
+#ifdef NEED_DECLARATION_STRCASECMP
+#include <string.h>
+int strcasecmp(const char *s1, const char *s2);
+#endif
+
+/* Declaration for siginterrupt(3)? */
+#undef NEED_DECLARATION_SIGINTERRUPT
+#ifdef NEED_DECLARATION_SIGINTERRUPT
+#include <signal.h>
+int siginterrupt(int sig, int flag);
+#endif
+
+/* Declaration for popen(3)? */
+#undef NEED_DECLARATION_POPEN
+#ifdef NEED_DECLARATION_POPEN
+#include <stdio.h>
+FILE *popen(const char *command, const char *type);
+#endif
+
+/* Declaration for pclose(3)? */
+#undef NEED_DECLARATION_PCLOSE
+#ifdef NEED_DECLARATION_PCLOSE
+#include <stdio.h>
+int pclose(FILE *stream);
+#endif
+
+#endif /* !DONT_NEED_DECLARATIONS */
+
 /* snprintf(3) unsafe emulation */
 #ifdef HAVE_SNPRINTF
-#define VARPRINTF(v,f,d) snprintf((v),sizeof((v)),f,(d))
+#define VARPRINTF(v,f,d)               snprintf((v),sizeof((v)),f,(d)                     )
+#define VARPRINTF2(v,f,d1,d2)          snprintf((v),sizeof((v)),f,(d1),(d2)               )
+#define VARPRINTF5(v,f,d1,d2,d3,d4,d5) snprintf((v),sizeof((v)),f,(d1),(d2),(d3),(d4),(d5))
 #else
-#define VARPRINTF(v,f,d) sprintf((v),f,(d))
+#define VARPRINTF(v,f,d)               sprintf((v),f,(d)                     )
+#define VARPRINTF2(v,f,d1,d2)          sprintf((v),f,(d1),(d2)               )
+#define VARPRINTF5(v,f,d1,d2,d3,d4,d5) sprintf((v),f,(d1),(d2),(d3),(d4),(d5))
 #endif
 
 /* vsnprintf(3) unsafe emulation */
 #ifdef HAVE_VSNPRINTF
-#define VARVPRINTF(v,f,d) vsnprintf((v),sizeof((v)),f,(d))
+#define VARVPRINTF(v,f,d)      vsnprintf((v),sizeof((v)),f,(d)      )
+#define VARVPRINTF2(v,f,d1,d2) vsnprintf((v),sizeof((v)),f,(d1),(d2))
 #else
-#define VARVPRINTF(v,f,d) vsprintf((v),f,(d))
+#define VARVPRINTF(v,f,d)      vsprintf((v),f,(d)      )
+#define VARVPRINTF2(v,f,d1,d2) vsprintf((v),f,(d1),(d2))
+#endif
+
+#ifndef HAVE_STRERROR
+extern char *sys_errlist[];
+#define strerror(i) sys_errlist[i]
 #endif
 
+#ifndef HAVE_STRCHR
+#define strchr index
+#endif
+
+#ifndef HAVE_STRRCHR
+#define strrchr rindex
+#endif
+
+#ifdef HAVE_STRCASECMP
+#define strtrycasecmp strcasecmp
+#else
+#define strtrycasecmp strcmp
+#endif
+
+#ifndef HAVE_OFFSETOF
+#define offsetof(a,b) ((size_t)&((a *)0)->b)
+#endif
index deb107a..9589652 100755 (executable)
@@ -5,10 +5,10 @@
 
 CONFDEFS="--enable-maintainer-mode --enable-debug"
 
-if [ -f Makefile ];then touch ChangeLog;make maintainer-clean;fi
-VER="`sed <configure.in -n 's/^AM_INIT_AUTOMAKE(.*, *\([^ ]*\) *)$/\1/p'`"
-echo "Detected version: $VER"
+# if [ -f Makefile ];then touch ChangeLog;make maintainer-clean;fi
 rm -r -f \
+       `find -name "*~"` \
+       errs* \
        install-sh \
        mkinstalldirs \
        missing \
@@ -32,23 +32,40 @@ rm -r -f \
        getopt.c \
        getopt.h \
        getopt1.c \
-       mdsms-$VER.tar.gz \
-       mdsms-$VER.tar.Z \
+       need-declaration.m4 \
+       strdup.c \
+       usleep.c \
+       memmove.c \
+       mdsms.1 \
+       mdsms.1.in2 \
+       mdsms \
+       mdsms-[0-9]*.tar.gz \
+       mdsms-[0-9]*.tar.Z \
        manfmt \
        .deps \
        ChangeLog \
+       *.o \
 
 
 if [ "$1" = clean ];then exit;fi
 
-cp ../getopt/getopt{1.c,.{c,h}} .
+       d="`pwd`"
+       (cd $HOME/src/misc
+        (cd getopt
+               cp getopt.c getopt.h getopt1.c $d
+        )
+        (cd macros
+               cp need-declaration.m4 $d
+        )
+        cp strdup.c usleep.c memmove.c $d
+       )
+
 aclocal -I .
 autoheader
 touch ChangeLog
 automake --gnu -c --add-missing
-#--include-deps
 rm -f ChangeLog
-cp ../LGPL2.1 COPYING
+cp $HOME/src/misc/LGPL2.1 COPYING
 autoheader
 autoconf
 
index 979a463..0777284 100644 (file)
@@ -2,7 +2,7 @@
 
 dnl Process this file with autoconf to produce a configure script.
 AC_INIT(mdsms.c)
-AM_INIT_AUTOMAKE(mdsms, 1.3)
+AM_INIT_AUTOMAKE(mdsms, 1.5.0)
 AM_CONFIG_HEADER(config.h)
 AM_MAINTAINER_MODE
 
@@ -98,6 +98,10 @@ if test "$with_getopt" = yes -o "$ac_cv_func_getopt_long" != yes ;then
 else AC_MSG_RESULT(no)
 fi
 
+AC_CHECK_HEADERS(assert.h ctype.h errno.h fcntl.h limits.h signal.h stdarg.h)
+AC_CHECK_HEADERS(stdio.h stdlib.h string.h sys/poll.h sys/stat.h sys/time.h)
+AC_CHECK_HEADERS(sys/types.h termios.h time.h unistd.h)
+
 AC_CHECK_FUNC(MAX, AC_DEFINE(HAVE_MAX) ,
        AC_CHECK_FUNC(max, AC_DEFINE(MAX, max) AC_DEFINE(HAVE_MAX))
        )
@@ -107,26 +111,52 @@ AC_CHECK_FUNC(MIN, AC_DEFINE(HAVE_MIN) ,
        )
 
 AC_MSG_CHECKING([for LINE_MAX])
-AC_TRY_COMPILE([#include <limits.h>], [LINE_MAX;],
+AC_TRY_COMPILE([
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif], [LINE_MAX;],
        AC_DEFINE(HAVE_LINE_MAX)  AC_MSG_RESULT(yes),
        AC_DEFINE(LINE_MAX, 4096) AC_MSG_RESULT(no))
 
 AC_MSG_CHECKING([for CBAUD])
-AC_TRY_COMPILE([#include <termios.h>], [CBAUD;],
+AC_TRY_COMPILE([
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif], [CBAUD;],
        AC_DEFINE(HAVE_CBAUD) AC_MSG_RESULT(yes),
        AC_DEFINE(CBAUD, 0)   AC_MSG_RESULT(no))
 
 AC_MSG_CHECKING([for CBAUDEX])
-AC_TRY_COMPILE([#include <termios.h>], [CBAUDEX;],
+AC_TRY_COMPILE([
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif], [CBAUDEX;],
        AC_DEFINE(HAVE_CBAUDEX) AC_MSG_RESULT(yes),
        AC_DEFINE(CBAUDEX, 0)   AC_MSG_RESULT(no))
 
+AC_MSG_CHECKING([for FD_SETSIZE])
+AC_TRY_COMPILE([
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif], [FD_SETSIZE;],
+       AC_DEFINE(HAVE_FD_SETSIZE) AC_MSG_RESULT(yes),
+                                  AC_MSG_RESULT(no))
+
 AC_MSG_CHECKING([for unused attribute])
 uns=true
 for un in __unused__ unused;do
   if $uns;then
                unx="__attribute__ (($un))"
-               AC_TRY_COMPILE([#include <stddef.h>],[char untest $unx;],
+               AC_TRY_COMPILE([
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif],[char untest $unx;],
                AC_DEFINE_UNQUOTED(ATTR_UNUSED, $unx)
                AC_MSG_RESULT($un)
                uns=false)
@@ -134,35 +164,92 @@ for un in __unused__ unused;do
        done
 if $uns;then AC_DEFINE(ATTR_UNUSED,) AC_MSG_RESULT(no);fi
 
+AC_MSG_CHECKING([for printf style attribute])
+AC_TRY_COMPILE([
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+void testf(char *fmt,...) __attribute__((format(printf,1,2)));
+void testf(char *fmt,...) {}], [testf("%d",1);],
+       AC_DEFINE(HAVE_PRINTFORMAT) AC_MSG_RESULT(yes),
+                                   AC_MSG_RESULT(no)
+       )
+
+AC_MSG_CHECKING([for offsetof macro])
+AC_TRY_COMPILE([
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+struct x { int a; };], [return(offsetof(struct x,a));],
+       AC_DEFINE(HAVE_OFFSETOF) AC_MSG_RESULT(yes),
+                                AC_MSG_RESULT(no)
+       )
+
 AC_CHECK_FUNC(snprintf, AC_DEFINE(HAVE_SNPRINTF))
 AC_CHECK_FUNC(vsnprintf, AC_DEFINE(HAVE_VSNPRINTF))
 
+dnl FIXME: select(3) missing here but what to do anyway
+AC_CHECK_FUNCS(siginterrupt strerror strchr strrchr memmove atexit strcasecmp poll)
+
 AC_TYPE_SIGNAL
 AC_TYPE_PID_T
 
-AC_REPLACE_FUNCS(strdup)
-dnl AC_MSG_CHECKING([for strdup])
-dnl AC_TRY_COMPILE([#include <string.h>], [strdup("");],
-dnl    AC_DEFINE(HAVE_STRDUP)  AC_MSG_RESULT(yes),
-dnl    LIBOBJS="$LIBOBJS strdup.o" AC_MSG_RESULT(no))
-
-AC_MSG_CHECKING([for working %m in printf])
-AC_TRY_RUN([int main() { char s[100];
-       sprintf(s,"%m");
-       return(!(strcmp(s,"m") && strcmp(s,"%m")));
-       }],AC_DEFINE(PRINTF_WORKS_PM) AC_MSG_RESULT(yes),AC_MSG_RESULT(no),AC_MSG_RESULT(avoiding))
-
-GCC_NEED_DECLARATION(gethostname, [#include <unistd.h>])
-GCC_NEED_DECLARATION(kill,        [#include <sys/types.h>
-#include <signal.h>])
-GCC_NEED_DECLARATION(snprintf,    [#include <stdio.h>])
-GCC_NEED_DECLARATION(vsnprintf,   [#include <stdio.h>
-#include <stdarg.h>])
-GCC_NEED_DECLARATION(strdup,      [#include <string.h>])
-GCC_NEED_DECLARATION(usleep,      [#include <unistd.h>])
+dnl FIXME: mktime(3) missing here
+AC_REPLACE_FUNCS(strdup usleep)
+
+GCC_NEED_DECLARATION(gethostname,[
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif])
+GCC_NEED_DECLARATION(kill,[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif])
+GCC_NEED_DECLARATION(snprintf,[
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif])
+GCC_NEED_DECLARATION(vsnprintf,[
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif])
+GCC_NEED_DECLARATION(strdup,[
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif])
+GCC_NEED_DECLARATION(usleep,[
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif])
+GCC_NEED_DECLARATION(strcasecmp,[
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif])
+GCC_NEED_DECLARATION(siginterrupt,[
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif])
+GCC_NEED_DECLARATION(popen,[
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif])
+GCC_NEED_DECLARATION(pclose,[
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif])
 
 # Final output.
 
+if test "$ACLOCAL" = "aclocal";then
+  ACLOCAL="$ACLOCAL -I ."
+fi
+
 AC_SUBST(LIBOBJS)
 
 AC_OUTPUT(Makefile)
index 47092a8..03b8303 100644 (file)
--- a/manfmt.c
+++ b/manfmt.c
@@ -3,10 +3,18 @@
 static char rcsid[] ATTR_UNUSED = "$Id$";
 #endif
 
+#ifdef HAVE_STDIO_H
 #include <stdio.h>
-#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
 #include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_LIMITS_H
 #include <limits.h>
+#endif
 
 static char buf[LINE_MAX];
 
@@ -24,6 +32,7 @@ char f;
                        if (*s=='\t') {
                                if ((f=!f)) {
 /*fprintf(stderr,"DURING TURN-ON:%s",s);*/
+                                       while (s[1]==' ') s++;
                                        if (s[1]=='"') s++;
                                        }
                                else {
index 23d8ae1..8ccf30d 100644 (file)
--- a/mdsms.1.c
+++ b/mdsms.1.c
@@ -2,6 +2,7 @@
  * $Id$
  */
 
+#define DONT_NEED_DECLARATIONS 1
 #include "config.h"
 #include "setup.h"
-#include "mdsms.1.in"
+#include "mdsms.1.in2"
index d645d68..43f2226 100644 (file)
@@ -1,27 +1,55 @@
-.TH MDSMS 1 "17 May 1999" ~    VERSION ~ "SendSMS Manual"
+.TH MDSMS 1 "30 Oct 1999" ~    VERSION ~ "MDSMS Manual"
 
 .\~ $Id$
 
 .SH NAME
-mdsms \- send SMSes through MobilDock
+mdsms \- Mobile Device SMS tool
 
 .SH SYNOPSIS
-.B mdsms
+.BR "mdsms --send" [ -mobildock ]
+.RB options...
+.RB [ "\-f" ]
+.BR "<dest. phone>"
+.BR "<msg text>"
+.br
+.BR "mdsms --receive"
+.RB options...
+.BR "<command name>"
+.br
+.BR "mdsms --logo-send"
+.RB options...
+.BR "<dest. phone>"
+.BR "<logo filename>"
+.RB [ "<GSMnet id>" ]
+.br
+options:
 .RB [ "-c <cfgfile>" ]
 .RB [ "-d <device>" ]
 .RB [ "-L <file>" ]
+.RB [ "-b <rate>" ]
 .RB [ "-l <lockfile>" ]
 .RB [ "-s <smsc #>" ]
 .RB [ "-m <#>" ]
 .RB [ "-r <sec>" ]
 .RB [ "-t <msec>" ]
 .RB [ "-T <msec>" ]
-.RB [ "\-fvhV" ]
-.RB [ "<dest. phone>" ]
-.RB [ "<msg text>" ]
+.RB [ "\-vhV" ]
 
 .SH DESCRIPTION
-Program sends one SMS message through MobilDock device.
+Program sends one SmartMessaging SMS through one of the supported mobile
+device connected through the serial port. Currently supported:
+.RS
+.TP
+.B Nokia Communicator 9000/9000i
+All modes except \fB--logo-send\fP and Nokia multiparts supported.
+.TP
+.B Nokia Communicator 9110
+All modes except Nokia multiparts supported. This is the only device
+with \fB--logo-send\fP capability for now.
+.TP
+.B Siemens M20
+Firmware revision 2.0 required. All modes except \fB--logo-send\fP supported.
+.RE
 
 .SH OPTIONS
 .TP
@@ -31,7 +59,7 @@ Process recursively this file and read all options from it. See the section
 for more information.
 .TP
 .BR -d | --device\  < device >
-Specify serial device to communicate with MobilDock. If only bare name is
+Specify serial device to communicate with your mobile. If only bare name is
 specified, "\fB/dev/\fP" is prepended automatically. This device name is used
 subsequently with
 .BR -l | --lockfile
@@ -44,6 +72,15 @@ etc., similar to output of
 If set to empty string (which is default - ~\fB        DEF_LOGNAME     \fP~), nothing
 is logged anywhere.
 .TP
+.BR -b | --baud\  < rate >
+Sets custom baudrate for accessing Nokia modem. Supported speeds are currently:
+\fB2400\fP,
+\fB4800\fP,
+\fB9600\fP,
+\fB19200\fP,
+\fB38400\fP,
+\fB57600\fP. Default is \fB    DEF_BAUD        \fP.
+.TP
 .BR -l | --lockfile\  < lockfile >
 Prior to accessing serial device specified above the lockfile should be
 acquired for correct concurrent processes behaviour. Although this name
@@ -58,7 +95,7 @@ Specify custom SMS center number. If not specified (or overriden as empty
 string by \fB-s ""\fP)
 .B mdsms
 asks by "\fBAT+CSCA?\fP"
-Siemens A1 for its default SMS center. Situation with undeterminable SMS center
+for the current default SMS center. Situation with undeterminable SMS center
 is unrecoverable and casues immediate fail. It is a common practice to use
 plus sign ("\fB+\fP") to indicate international number type.
 Known SMS centers as of May 1999:
@@ -74,16 +111,18 @@ Maximum retries of any \fBAT\fP-style command during the session. All the
 retries are summed during one run of
 .B mdsms
 and no more retries of the command are permitted. After exceeding this
-value the program is terminated.
+value the program is terminated. Exception is \fB--receive\fP which is never
+quit after the initial initialisation has been successfuly negotiated.
 .TP
 .BR -r | --readtime\  < sec >
-Maximum response read timeout before give up.
+Maximum response read timeout before command retry.
 .B mdsms
 sends the requested \fBAT\fP-style command to
+<<<<<<< mdsms.1.in
 .B MobilDock
 and expects response. Unfortunately when the voice call is in progress,
 .B MobilDock
-will block any serial device communication and it is impossible to differentiate
+\80will block any serial device communication and it is impossible to differentiate
 between calling
 .B MobilDock
 and stucked/disconnected one. After exceeding this time interval,
@@ -91,8 +130,21 @@ and stucked/disconnected one. After exceeding this time interval,
 is terminated immediately,
 .B maxretry
 parameter notwithstanding. Standard value is
+=======
+.B Nokia
+and expects response. After exceeding this time interval,
+.B sms9110
+reissues the last command until the maximum retry count (\fBmaxretry\fP) is exhausted.
+Standard value is
+>>>>>>> 1.5.2.2
 .BR DEF_READTIME
-seconds and should be enough for any voice call.
+<<<<<<< mdsms.1.in
+\80seconds and should be enough for any voice call.
+=======
+seconds and should be enough for standard
+.B Nokia
+responses.
+>>>>>>> 1.5.2.2
 .TP
 .BR -t | --chartime\  < msec >
 Although the fixed used baudrate of
@@ -106,23 +158,35 @@ waiting a bit after character sent to give relax time to these devices.
 When full rate communication was used, occasional longer SMS data corruption
 was observed. Argument is given in milliseconds and its default value is
 .BR DEF_CHARTIME ms.
+\fB(This paragraph\fP
+.BR "doesn't"
+\fBapply to Nokia 9110 but the functionality has been
+retained just to be on the safe side.)\fP
 .TP
 .BR -T | --cmdtime\  < msec >
 This delay is given before sending any
 .B AT-style
-command to MobilDock/Siemens A1.
+command to Nokia.
 Its primary purpose is to let any previous entered commands to finish and
 to clear any input before actually sending our own command. Also all Siemens
 devices are known that they strongly dislike fast edge-to-edge communication
 and to satisfy these requirements this delay was considered as the best
-approach. The default value is
+approach.
+\fB(This sentence\fP
+.BR "doesn't"
+\fBapply to Nokia 9110 but the functionality has been
+retained just to be on the safe side.)\fP
+The default value is
 .BR DEF_CMDTIME ms.
 .TP
-.BR -f | --file
+<<<<<<< mdsms.1.in
+\80.BR -f | --file
 When parameter <\fBmsg text\fP> has been specified, by using this option
 \fBmdsms\fP will read the file with the <\fBmsg text\fP> filename instead
 and send its \fBcontents\fP as the SMS message.
 .TP
+=======
+>>>>>>> 1.5.2.2
 .BR -v | --verbose
 Increase verbosity level by one. Currently the maximum defined level is
 .BR 3 ,
@@ -139,7 +203,7 @@ Print the version number and exit.
 .TP
 .RB < "dest. phone" >
 This mandatory parameter specifies the telephone number of the recipient
-of SMS message. International prefix character plus
+of SmartMessaging message. International prefix character plus
 .RB ( + )
 is supported the national mode without plus
 .RB ( + )
@@ -151,7 +215,17 @@ This number can be made default in system configuration files, see below
 for section
 .BR CONFIGURATION .
 .TP
-.RB < "msg text" >
+.RB < "logo filename" >
+Here you specifify the filename of the logo file to be uploaded. Currently
+recognized file formats are \fBNOL\fP (Nokia logo?) and \fBNGG\fP (Nokia
+Group Graphics). These formats are proprietary by \fBKESSLER Wireless Design\fP
+and \fBsms9110\fP currently "can't" edit them. More info can be found on:
+.RS
+.B http:/      /www.kessler-design.com/wireless/operatorlogo.php3
+.RE
+.TP
+<<<<<<< mdsms.1.in
+\80.RB < "msg text" >
 Here you write the exact body of the message. This parameter should be
 specified only as one component, although if more found they are concatenated
 with separating space (" "). But this practice is discouraged as
@@ -161,9 +235,22 @@ mess, you may prefer to omit this parameter and the the message text is
 then read from
 .I stdin
 (standard input stream).
+=======
+.RB < "GSMnet id" >
+Specify GSM network code to be set on the logo being uploaded. Upon upload to
+Nokia phone each operator logo has a GSM network code binded with it. When
+you are registered (even roamed) into such network the logo is displayed on
+the phone. Current Nokia mobile phones can handle only one logo loaded
+simultaneously, it will be rewritten by any other upload. You can also
+specify string ~\fB    WORD_GROUP      \fP~ to send the logo as group graphics
+(even from \fBNOL\fP format) or string ~\fB    WORD_NET        \fP~ to force detection
+of network code from \fBNOL\fP. The default if this parameter is not specified
+is ~\fB        WORD_NET        \fP~ for \fBNOL\fP files and ~\fB       WORD_GROUP      \fP~ for
+\fBNGG\fP files.
+>>>>>>> 1.5.2.2
 
 .SH CONFIGURATION
-.B mdsms
+.B sms9110
 reads ~\fB     CONFIG_MAIN     \fP~ followed by ~\fB$HOME      CONFIG_HOME     \fP~
 configuration files. The content of these files (and also any file read by
 .BR -c | --config
@@ -187,23 +274,29 @@ to see details of option processing with more complex configuration setups.
 
 .SH OPERATION
 Upon startup
-.B mdsms
+.B sms9110
 locks the port (see option
 .BR -l | --lockfile
-for details) and opens the serial device with hardcoded parameters of
-19200 baud, software handshaking (XON/XOFF style), 8 bits, no parity.
-Then issues the following commands:
+for details) and opens the serial device with specified baudrate (default
+\fB    DEF_BAUD        \fP baud), software handshaking (XON/XOFF style), 8 bits,
+no parity. Then issues the following commands:
 .TP
-.BR AT <CTRL-Z>
+.BR AT <ESCAPE>
 .B AT
-is used to fool MobilDock and pass the following
+<<<<<<< mdsms.1.in
+\80is used to fool MobilDock and pass the following
 .B <CTRL-Z>
 (ascii decimal code 26) character to Siemens A1 and break it from eventual
+=======
+is used to fool Nokia and pass the following
+.B <ESCAPE>
+(ascii decimal code 27) character to Nokia and break it from eventual
+>>>>>>> 1.5.2.2
 .B AT+CMGS
 mode in which it may errorneously remain from previous sessions.
 .TP
 .B AT
-Test the responsiveness of Siemens A1.
+Test the responsiveness of Nokia.
 .TP
 \fBAT+CSCA="\fPsmc # from user\fB"\fP
 This command is omitted if
@@ -211,30 +304,54 @@ This command is omitted if
 is not specified by user (or specified/overriden as empty string \fB""\fP
 .TP
 \fBAT+CSCA?\fP
+<<<<<<< mdsms.1.in
 Query the currently set SMC center number to include it later to the
 header of SMS PDU format where it is required. If \fBAT+CSCA="\fP...\fB"\fP
 was issued before, this number should match it but no sanity checks are
 currently do so.
 .TP
-\fBAT+CMGF=0\fP
+\80\fBAT+CMGF=0\fP
 Set the default SMS message format to PDU type. Currently Siemens A1
 .RB "doesn't"
 support any other SMS format anyway.
+=======
+Query the currently set SMC center number to detect possibly unset SMS center.
 .TP
-\fBAT+CMGS=\fP# chars
+\fBAT+CSMP=81,,0,245\fP
+Sets PDU type to 81 (\fBSMS-SUBMIT\fP + integer-type for \fBvalidity\fP
++ \fBUDHI\fP - user data header indicator), PID (protocol identifier) to
+0 (standard non-converted SMS) and DCS (data coding scheme) to 0xF5
+(\fBdata coding\fP/\fBmessage class\fP, \fB8-bit data\fP + \fBmobile-equipment\fP
+specific).
+>>>>>>> 1.5.2.2
+.TP
+<<<<<<< mdsms.1.in
+\80\fBAT+CMGS=\fP# chars
+=======
+\fBAT+CMGS="\fPphone # from user\fB"\fP
+>>>>>>> 1.5.2.2
 This command physically sends the message and the resulting "\fB+CMGS:\fP" output
 is catched and returned as
 .B MR
 (message reference) number to the user.
 .TP
+\fBAT+CSMP=17,,0,0\fP
+Resets back PDU type to 17 (\fBSMS-SUBMIT\fP + integer-type for \fBvalidity\fP),
+PID (protocol identifier) to 0 (standard non-converted SMS) and DCS (data coding
+scheme) to 0 (\fBalphabet indication\fP, \fBdefault 7-bit alphabet\fP).
+.TP
 \fBAT\fP
-Check that Siemens A1 survived the sending of the message.
+Check that Nokia survived the sending of the message.
 
 .SH SEE ALSO
 .TP
 .B GSM 03.40
 ETSI documentation for SMS messages in GSM networks
 .TP
+.B GNokii, tools and drivers for Nokia mobile phones
+Nokia logo data format was read from its sources:
+.B http:/      www.gnokii.org/
+.TP
 .B "Developers'" Guide: SMS with the A1
 Tech note on PDU SMS format etc:
 .B http:/              /www.siemens.se/telefoner/ovrigtgsm/fragorsvar/a1_sms.pdf
@@ -252,7 +369,7 @@ Main configuration file
 User personalized local configuration file
 
 .SH AUTHOR
-.B mdsms
+.B sms9110
 was written by Jan Kratochvil who should be responsible for all the bugs
 included. Please see the file "\fBAUTHORS\fP"
 shipped with the original distribution archive for more details.
diff --git a/mdsms.c b/mdsms.c
index 8794249..845c629 100644 (file)
--- a/mdsms.c
+++ b/mdsms.c
@@ -5,23 +5,57 @@ static char rcsid[] ATTR_UNUSED = "$Id$";
 
 #include "setup.h"
 
+#ifdef HAVE_STDIO_H
 #include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
 #include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
 #include <string.h>
+#endif
+#ifdef HAVE_SIGNAL_H
 #include <signal.h>
+#endif
+#ifdef HAVE_STDARG_H
 #include <stdarg.h>
+#endif
+#ifdef HAVE_LIMITS_H
 #include <limits.h>
+#endif
+#ifdef HAVE_CTYPE_H
 #include <ctype.h>
-#include <string.h>
+#endif
+#ifdef HAVE_TERMIOS_H
 #include <termios.h>
+#endif
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
+#ifdef HAVE_ASSERT_H
 #include <assert.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
+#endif
+#ifdef HAVE_FCNTL_H
 #include <fcntl.h>
+#endif
+#ifdef HAVE_ERRNO_H
 #include <errno.h>
-#include <signal.h>
+#endif
+#ifdef HAVE_TIME_H
 #include <time.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
 
 #ifdef HAVE_GETOPT_LONG
 #include <getopt.h>
@@ -40,12 +74,16 @@ static char rcsid[] ATTR_UNUSED = "$Id$";
 #define dO stderr
 #define dB(a) dbg(fprintf a)
 
-#define d1(n1)          dB((dO,n1         ))
-#define d2(n1,n2)       dB((dO,n1,n2      ))
-#define d3(n1,n2,n3)    dB((dO,n1,n2,n3   ))
-#define d4(n1,n2,n3,n4) dB((dO,n1,n2,n3,n4))
+#define d1(n1)                      dB((dO,n1                     ))
+#define d2(n1,n2)                   dB((dO,n1,n2                  ))
+#define d3(n1,n2,n3)                dB((dO,n1,n2,n3               ))
+#define d4(n1,n2,n3,n4)             dB((dO,n1,n2,n3,n4            ))
+#define d5(n1,n2,n3,n4,n5)          dB((dO,n1,n2,n3,n4,n5         ))
+#define d6(n1,n2,n3,n4,n5,n6)       dB((dO,n1,n2,n3,n4,n5,n6      ))
+#define d7(n1,n2,n3,n4,n5,n6,n7)    dB((dO,n1,n2,n3,n4,n5,n6,n7   ))
+#define d8(n1,n2,n3,n4,n5,n6,n7,n8) dB((dO,n1,n2,n3,n4,n5,n6,n7,n8))
 
-static const char version[]="This is MobilDock SMS sender (" PACKAGE " " VERSION ")\n";
+static const char version[]="This is Mobile Device SMS tool (" PACKAGE " " VERSION ")\n";
 
 static int verbose
 #ifdef DEBUG
@@ -55,10 +93,32 @@ static int verbose
 static char *pname;
 static int dis_cleanup=0,devfd=-1;
 
-static char *phone,*body,*device,*logname,*lockfile,*smsc,*maxretry,*readtime,*chartime,*cmdtime;
+static char *phone,*device,*logname,*lockfile,*smsc,*maxretry,*readtime,*chartime,*cmdtime,*baud,*restore;
 static int readbody;
-static long maxretryn=DEF_MAXRETRY,readtimen=DEF_READTIME,chartimen=DEF_CHARTIME,cmdtimen=DEF_CMDTIME;
+static long maxretryn=DEF_MAXRETRY,readtimen=-1,chartimen=DEF_CHARTIME,cmdtimen=DEF_CMDTIME,baudn=DEF_BAUD;
 static size_t bodylen;
+/* --send / --send-mobildock / --receive specific */
+static char *body;
+/* --logo-send specific */
+static char *logoname,*gsmnet;
+
+static enum modenum {
+  MODE_UNKNOWN=0,
+/* must differ from regular char-s */
+  MODE_FIRST         =0x3400,
+       MODE_SEND          =MODE_FIRST+0, /* --send / --send-mobildock */
+       MODE_SEND_MOBILDOCK=MODE_FIRST+1, /* --send-mobildock in before readtimen is set */
+       MODE_RECEIVE       =MODE_FIRST+2, /* --receive */
+       MODE_LOGO_SEND     =MODE_FIRST+3  /* --logo-send */
+       } mode=MODE_UNKNOWN;
+#define MODE_ORDER(x) ((x)-MODE_FIRST)
+#define MODE_NAME(x) (longopts[MODE_ORDER((x))].name)
+#define MODE_BIT(x) (1<<MODE_ORDER((x)))
+
+static unsigned mode_stamp;
+
+/* pdusmsc variable has to be filled in */
+#define NEED_PDUSMSC() (mode==MODE_SEND)
 
 static char *devicename; /* path stripped */
 static char lockreal[512],locked;
@@ -67,11 +127,8 @@ static struct termios restios,tios;
 static char restios_yes;
 static FILE *logf;
 
-static void vlogmsg(
-#ifndef PRINTF_WORKS_PM
-               char outerr,
-#endif
-               const char *fmt,va_list ap)
+static void vlogmsg(char pm,char fatal,const char *fmt,va_list ap) ATTR_PRINTFORMAT(3,0);
+static void vlogmsg(char pm,char fatal,const char *fmt,va_list ap)
 {
 time_t stamp;
 char *ctm,*s;
@@ -88,94 +145,79 @@ char host[LINE_MAX];
        if ((s=strchr(ctm,'\n'))) *s='\0';
        fprintf(logf,"%s %s %s[%d]: ",ctm,host,pname,mypid);
        vfprintf(logf,fmt,ap);
-#ifndef PRINTF_WORKS_PM
-       if (outerr) fputs(strerror(errno),logf);
-#endif
+       if (pm) { fputs(": ",logf); fputs(strerror(errno),logf); }
+       if (fatal!='\n') fputc((fatal=='.'?'.':'!'),logf);
        fputc('\n',logf);
        fflush(logf);
 }
 
+static void logmsg(const char *fmt,...) ATTR_PRINTFORMAT(1,2);
 static void logmsg(const char *fmt,...)
 {
 va_list ap;
        va_start(ap,fmt);
-       vlogmsg(
-#ifndef PRINTF_WORKS_PM
-               0,
-#endif
-               fmt,ap);
+       vlogmsg(0,'\n',fmt,ap);
        va_end(ap);
 }
 
+static void error(const char *fmt,...) ATTR_PRINTFORMAT(1,2);
 static void error(const char *fmt,...)
 {
 va_list ap;
-char fatal=*fmt;
-#ifndef PRINTF_WORKS_PM
-char pm,*nfmt;
-size_t fmtl;
-#endif
+char fatal,pm;
 
+       if ((pm=(*fmt=='^'))) fmt++;
+       fatal=*fmt;
        if (fatal=='!' || fatal=='.' || fatal=='\n') fmt++;
        else fatal=0;
 
-#ifndef PRINTF_WORKS_PM
-       if (!(nfmt=strdup(fmt))) return;
-       fmtl=strlen(fmt);
-       if ((pm=(fmtl>=2 && !strcmp(fmt+fmtl-2,"%m")))) nfmt[fmtl-2]='\0';
-#endif
-
        fprintf(stderr,"%s: ",pname);
        va_start(ap,fmt);
-       vfprintf(stderr,
-#ifdef PRINTF_WORKS_PM
-               fmt
-#else
-               nfmt
-#endif
-               ,ap);
-       if (fatal=='!') vlogmsg(
-#ifdef PRINTF_WORKS_PM
-               fmt
-#else
-               pm,nfmt
-#endif
-               ,ap);
+       vfprintf(stderr,fmt,ap);
+       if (fatal=='!') vlogmsg(pm,fatal,fmt,ap);
        va_end(ap);
-
-#ifndef PRINTF_WORKS_PM
-       if (pm) {
-               fputs(strerror(errno),stderr);
-               free(nfmt);
-               }
-#endif
-
+       if (pm) { fputs(": ",stderr); fputs(strerror(errno),stderr); }
        if (fatal!='\n') fputc((fatal=='.'?'.':'!'),stderr);
        fputc('\n',stderr);
        if (fatal=='!') exit(EXIT_FAILURE);
 }
 
-static void chk(void *p)
+static void chk(const void *p)
 {
        if (p) return;
        error("!Virtual memory exhausted");
 }
 
+static char *devcmd(const char *term,const char *catch,const char *send,...) ATTR_PRINTFORMAT(3,4);
+
+static void unlockdevice(int hard)
+{
+       d2("unlockdevice(), locked=%d\n",locked);
+       if (!locked || !*lockreal) return;
+       if (!hard && locked>1) { locked--; return; }
+       d2("Removing lockfile \"%s\"\n",lockreal);
+       if (unlink(lockreal))
+               error("^Error removing my device lockfile \"%s\"",lockreal);
+       locked=0;
+}
+
 static void cleanup(void)
 {
        d1("cleanup()\n");
        if (dis_cleanup) return;
+       if (restore) {
+char *cmd=restore;
+               restore=NULL;
+               devcmd(NULL,NULL,"\r\nAT");
+               devcmd(NULL,NULL,cmd);
+               devcmd(NULL,NULL,"\r\nAT");
+               }
        if (restios_yes) {
                if (tcsetattr(devfd,TCSANOW,&restios))
-                       error("Error restoring termios for device: %m");
+                       error("^Error restoring termios for device");
                restios_yes=0;
                }
-       if (locked && *lockreal) {
-               d2("Removing lockfile \"%s\"\n",lockreal);
-               if (unlink(lockreal))
-                       error("Error removing my device lockfile \"%s\": %m",lockreal);
-               locked=0;
-               }
+       unlockdevice(1);
        dis_cleanup=1;
        exit(EXIT_FAILURE);
 }
@@ -187,35 +229,69 @@ static void usage(void)
 %s\
 \n\
 Usage: " PACKAGE " [-c|--config <cfgfile>] [-d|--device <device>]\n\
-             [-L|--log <file>]\n\
+             {--send | --send-mobildock | --receive | --logo-send}\n\
+             [-L|--log <file>] [-b|--baud <rate>]\n\
              [-l|--lockfile <lock>] [-s|--smsc <smsc #>] [-m|--maxretry <#>]\n\
              [-r|--readtime <sec>] [-t|--chartime <msec>] [-T|--cmdtime <msec>]\n\
-             [-f|--file] [-v|--verbose] [-h|--help] [-V|--version]\n\
-             <dest. phone> <msg text|msg filename>\n\
+             [-v|--verbose] [-h|--help] [-V|--version]\n\
+  --send / --send-mobildock:\n\
+             [-f|--file] <dest. phone> <msg text|msg filename>\n\
+  --receive:\n\
+             <command name>\n\
+  --logo-send:\n\
+             <dest. phone> <logo filename> [<GSMnet id>]\n\
 \n\
  -c, --config\tRead this additional config file\n\
 \t\t(def. \"" CONFIG_MAIN "\" and \"$HOME" CONFIG_HOME "\")\n\
- -d, --device\tMobilDock on this serial device (def. \"" DEF_DEVICE "\")\n\
+ -d, --device\tMobile on this serial device (def. \"" DEF_DEVICE "\")\n\
  -L, --log\tLog all important messages to this file (def. \"" DEF_LOGNAME "\")\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\
- -s, --smsc\tUse this SMS Center number (def. query from Siemens A1)\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 (def. %ds)\n\
- -t, --chartime\tMilliseconds between each char on baud 19200 (def. %dms)\n\
+ -r, --readtime\tSeconds for maximum wait time for response\n\
+\t\t(def. %ds standard, %ds for MobilDock modes, multiplied %dx for long cmds)\n\
+ -t, --chartime\tMilliseconds between each char (def. %dms)\n\
  -T, --cmdtime\tMilliseconds before each whole AT command (def. %dms)\n\
- -f, --file\tRead contents of message from file instead\n\
  -v, --verbose\tIncrease verbosity level, more \"-v\"s give more messages\n\
  -h, --help\tPrint a summary of the options\n\
  -V, --version\tPrint the version number\n\
-\n",version,DEF_LOCKFILE,DEF_MAXRETRY,DEF_READTIME,DEF_CHARTIME,DEF_CMDTIME);
+\n\
+--send / --send-mobildock:\n\
+ -f, --file\tRead contents of message from file instead\n\
+--receive:\n\
+ <command name>\tProgram to run on receive, message will be on stdin\n\
+\t\tFollowing substitutes are recognized:\n\
+\t\t%%p - source phone number\n\
+\t\t%%T - timestamp from SMSC as number of seconds from 1970\n\
+\t\t%%t - ctime(3) style timestamp (e.g. \"Wed Jun 30 21:49:08 1993\")\n\
+--logo-send:\n\
+ <GSMnet id>\t* Oper. logo: Enter custom network code MccMnc, e.g. 23002\n\
+\t\t* Oper. logo: Specify \"" WORD_NET "\" to read network code from NOL file\n\
+\t\t* Group gfx : Specify \"" WORD_GROUP "\" to send logo as group graphics\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,DEF_BAUD,DEF_LOCKFILE,DEF_MAXRETRY,
+DEF_READTIME,DEF_READTIME_MOBILDOCK,EXT_READTIME,DEF_CHARTIME,DEF_CMDTIME);
        exit(EXIT_FAILURE);
 }
 
 static const struct option longopts[]={
+/* Modes has to be in-order on exact positions */
+{"send"          ,0,0,MODE_SEND},
+{"send-mobildock",0,0,MODE_SEND_MOBILDOCK},
+{"receive"       ,0,0,MODE_RECEIVE},
+{"logo-send"     ,0,0,MODE_LOGO_SEND},
+/* Mode aliases may follow in no particular order *
+ * as long as no non-mode options is between them */
+{"send-md"       ,0,0,MODE_SEND_MOBILDOCK},
+{"recv"          ,0,0,MODE_RECEIVE},
 {"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'},
@@ -235,7 +311,7 @@ static unsigned cfgstacki=0;
 static void chkfclose(FILE *f,const char *fname)
 {
        if (fclose(f))
-               error("Error closing \"%s\": %m",fname);
+               error("^Error closing \"%s\"",fname);
 }
 
 static void readfile(const char *fname,char quiet)
@@ -253,16 +329,17 @@ static unsigned tot=0;
                return;
                }
        if (!(f=fopen(fname,"rt"))) {
-               if (!quiet) error("Can't open config file \"%s\" for r/o: %m",fname);
+               if (!quiet) error("^Can't open config file \"%s\" for r/o",fname);
                return;
                }
+               
        if (verbose>=2) error(".Reading config file \"%s\"",fname);
        if (fseek(f,0,SEEK_END))
-               error("Error seeking to end of \"s\": %m",fname);
+               error("^Error seeking to end of \"%s\"",fname);
        if ((size=ftell(f))<0)
-               size=0,error("Error measuring \"%s\": %m",fname);
+               size=0,error("^Error measuring \"%s\"",fname);
        if (size>MAXCONFIG) 
-               error("File \"%s\" is too long, read only %ld bytes",fname,MAXCONFIG);
+               error("File \"%s\" is too long, read only %d bytes",fname,MAXCONFIG);
        chk(buf=malloc((size?size:MAXCONFIG)+1));
        rewind(f);
        got=fread(buf,1,(size?size:MAXCONFIG),f);
@@ -304,6 +381,109 @@ char c=*s;
        free(buf);
 }
 
+static struct argstack {
+       struct argstack *next;
+       int num,offset;
+       const char *from;
+       char *arg[1];
+       } *argstack;
+
+static struct argstack **argstack_tail=&argstack;
+static size_t lastargstack_len;
+static const char *lastargstack_from;
+static int lastargstack_index;
+
+static size_t argstack_size;
+static int argstack_num;
+
+static void pushargstack(char **args,int num,int offset,const char *from,char stack)
+{
+struct argstack *as;
+int i;
+
+       if (!num) return;
+       assert(num>=1);
+       chk(as=malloc(sizeof(*as)+sizeof(as->arg)*(num-1)));
+       as->num=num;
+       as->offset=offset;
+       if (!from) as->from=NULL;
+       else chk(as->from=strdup(from));
+       for (i=0;i<num;i++) {
+               chk(as->arg[i]=strdup(args[i]));
+               argstack_size+=strlen(args[i]);
+               }
+       argstack_num+=num;
+       if (stack) {
+               as->next=argstack;
+               argstack=as;
+               }
+       else {
+               as->next=NULL;
+               *argstack_tail=as;
+               argstack_tail=&as->next;
+               }
+}
+
+static void pushargstack_one(char *s,char stack)
+{ pushargstack(&s,1,0,NULL,stack); }
+
+static char *nextargstack(void)
+{
+static int order=0;
+char *r;
+
+       if (argstack && order==argstack->num) {
+struct argstack *as=argstack;
+               if (!(argstack=as->next))
+                       argstack_tail=&argstack;
+               order=0;
+               free((char *)as->from);
+               lastargstack_from=NULL;
+               free(as);
+               }
+       if (!argstack) {
+               assert(!argstack_num); assert(!argstack_size);
+               return(NULL);
+               }
+       assert(order<argstack->num);
+       lastargstack_index=argstack->offset+order;
+       r=argstack->arg[order++];
+       assert(argstack_num>0); argstack_num--;
+       lastargstack_len=strlen(r);
+       assert(argstack_size>=lastargstack_len); argstack_size-=lastargstack_len;
+       lastargstack_from=argstack->from;
+       return(r);
+}
+
+static char *glueargstack(size_t *destlenp,const char *glue)
+{
+size_t gluel=(glue?strlen(glue):0),destlen;
+char *dest,*d,*s;
+
+       if (!argstack_num) {
+               chk(dest=strdup(""));
+               if (destlenp) *destlenp=0;
+               return(dest);
+               }
+       destlen=argstack_size+(argstack_num-1)*gluel;
+       if (destlenp) *destlenp=destlen;
+       chk(dest=malloc(destlen+1));
+       for (d=dest;(s=nextargstack());) {
+               memcpy(d,s,lastargstack_len);
+               free(s);
+               d+=lastargstack_len;
+               if (!argstack_num) break;
+               if (!glue) continue;
+               memcpy(d,glue,gluel);
+               d+=gluel;
+               assert(d<=dest+destlen);
+               }
+       assert(!argstack_num);
+       assert(d==dest+destlen);
+       *d='\0';
+       return(dest);
+}
+
 static struct {
        const char c;
        char **const var;
@@ -311,6 +491,7 @@ static struct {
        } optset[]={
                { 'd',&device   },
                { 'L',&logname  },
+               { 'b',&baud     },
                { 'l',&lockfile },
                { 's',&smsc     },
                { 'm',&maxretry },
@@ -327,15 +508,15 @@ int i;
 
        seq++;
        optarg=NULL; optind=0; /* FIXME: Possible portability problem. */
-       while ((optc=getopt_long(argp,args,"c:d:L:l:s:m:r:t:T:fvhV",longopts,NULL))!=EOF) switch (optc) {
+       while ((optc=getopt_long(argp,args,"c:d:L:b:l: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),optind);
+                               error("Looping (%d) during attempt to read config file \"%s\", break-out",NELEM(cfgstack),from);
                                break;
                                }
                        chk(cfgstack[cfgstacki++]=strdup(optarg));
                        break;
-               case 'd': case 'L': case 'l': case 's': case 'm': case 'r': case 't': case 'T':
+               case 'd': case 'L': case 'b': case 'l': 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) {
@@ -349,6 +530,14 @@ int i;
                                        }
                        assert(i<NELEM(optset));
                        break;
+               case MODE_SEND:
+               case MODE_SEND_MOBILDOCK:
+               case MODE_RECEIVE:
+               case MODE_LOGO_SEND:
+                       if (mode_stamp && mode_stamp!=seq) break;
+                       mode=optc;
+                       mode_stamp=seq;
+                       break;
                case 'f':
                        readbody++;
                        break;
@@ -364,26 +553,7 @@ int i;
                        usage();
                        break;
                }
-       if (!phone && optind<argp)
-               chk(phone=strdup(args[optind++]));
-       if (!body && optind<argp) {
-char *d;
-int i;
-
-               for (i=optind,bodylen=argp-optind;i<argp;i++)
-                       bodylen+=strlen(args[i]);
-               chk(body=malloc(bodylen));
-               for (d=body,i=optind;i<argp;i++) {
-size_t l=strlen(args[i]);
-                       memcpy(d,args[i],l);
-                       d+=l;
-                       *d++=' ';
-                       assert(d<=body+bodylen);
-                       }
-               assert(d==body+bodylen);
-               d[-1]='\0';
-               bodylen--;
-               }
+       pushargstack(args+optind,argp-optind,optind,from,1);
        while (cfgstacki) {
 char *s=cfgstack[--cfgstacki];
 
@@ -394,17 +564,130 @@ char *s=cfgstack[--cfgstacki];
                }
 }
 
-static const struct {
+static const struct nullcheck {
        char **var;
+       enum modenum reqd;
        const char *name;
        } nullcheck[]={
-               {&phone,"destination phone number"},
+               {&phone,MODE_BIT(MODE_SEND)|MODE_BIT(MODE_SEND_MOBILDOCK)|MODE_BIT(MODE_LOGO_SEND),
+                       "destination phone number"},
+               {&logoname,MODE_BIT(MODE_LOGO_SEND),"logo filename"},
+               {&body,MODE_BIT(MODE_RECEIVE),"body text"}, /* we allow empty bodies for SENDs */
 #if 0
-               {&body ,"body text"},
-               {&device,"device for communication"},
+               {&gsmnet,"GSM operator network code",MODE_LOGO_SEND},
+               {&device,"device for communication",0},
 #endif
        };
-static char **emptycheck[]={&logname,&smsc,&body};
+static char **emptycheck[]={&logname,&smsc,&logoname,&gsmnet};
+
+static inline void emptyclean(void)
+{
+int i;
+
+       for (i=0;i<NELEM(emptycheck);i++)
+               if (*emptycheck[i] && !**emptycheck[i]) {
+                       free(*emptycheck[i]);
+                            *emptycheck[i]=NULL;
+                       }
+}
+
+static inline void cmdline_done(void)
+{
+char *s;
+       while ((s=nextargstack())) {
+               error("\nExcessive option %d from \"%s\" ignored: %s",
+                       lastargstack_index,lastargstack_from,s);
+               free(s);
+               }
+       emptyclean();
+}
+
+static char *check_phone(const char *phone)
+{
+const char *s,*s1;
+static char err[LINE_MAX];
+
+       for (s=s1=(phone+(*phone=='+'));*s && s-s1<MAXNUMLEN;s++)
+               if (!isdigit(*s)) {
+                       VARPRINTF2(err,"Invalid digit '%c' in phone number - at offset %d",
+                               *s,s-phone);
+                       return(err);
+                       }
+       if (!*s) return(NULL);
+       VARPRINTF2(err,"Phone number too long (%d), max. %d digits allowed",
+               strlen(s1),MAXNUMLEN);
+       return(err);
+}
+
+static void cmdline_phone(void)
+{
+       if (!phone && (phone=nextargstack())) {
+char *s;
+
+               if ((s=check_phone(phone)))
+                       error("!%s in option %d from \"%s\": %s",
+                                       s,lastargstack_index,lastargstack_from,phone);
+               }
+}
+
+static inline void cmdline_receive(void)
+{
+char *s;
+
+       if (!body && argstack_num) {
+               body=glueargstack(&bodylen," ");
+               for (s=body;(s=strchr(s,'%'));s++)
+                       switch (*++s) {
+                               case 'p': case 'T': case 't': break;
+                               default:
+                                       error("Unknown formatsymbol '%c', use (\"%%%%%c\" to fix it) at pos %d in: %s",
+                                               *s,*s,s-body,body);
+                               }
+               }
+}
+
+static inline void cmdline_send(void)
+{
+       cmdline_phone();
+       if (!body && argstack_num)
+               body=glueargstack(&bodylen," ");
+}
+
+static inline void cmdline_logo_send(void)
+{
+char *ogsmnet;
+
+       cmdline_phone();
+       if (!logoname) logoname=nextargstack();
+       if (!gsmnet && (ogsmnet=nextargstack())) {
+char *s,*d,e=0;
+
+               chk(gsmnet=strdup(ogsmnet));
+               if (strtrycasecmp(gsmnet,WORD_NET) && strtrycasecmp(gsmnet,WORD_GROUP)) {
+                       for (d=s=gsmnet;*s;s++) {
+                               if (isdigit(*s)) { *d++=*s; continue; }
+                               if (isspace(*s)) continue;
+                               error("\nInvalid characted '%c' in GSMnet at offs %d: %s",
+                                       *s,s-gsmnet,ogsmnet);
+                               e=1;
+                               break;
+                               }
+                       if ((d-gsmnet)!=5) {
+                               error("\nGSMnet is required to have exactly 5 digits or to be\n\
+either \"" WORD_NET "\" or \"" WORD_GROUP "\", but found length %d: %s",
+                                       d-gsmnet,ogsmnet);
+                               e=1;
+                               }
+                       if (!e) *d='\0';
+                       else {
+                               error("\nGSMnet option %d from \"%s\" rejected due to previous errors: %s",
+                                       lastargstack_index,lastargstack_from,ogsmnet);
+                               free(gsmnet);
+                               gsmnet=NULL;
+                               }
+                       }
+               }
+}
 
 static void lockclose(int fd)
 {
@@ -412,7 +695,7 @@ static void lockclose(int fd)
                error("Error closing lockfile \"%s\"",lockreal);
 }
 
-static inline void lockdevice(void)
+static inline int lockdevice(int attempt)
 {
 int fd=-1;
 char buf[64];
@@ -421,6 +704,8 @@ int delay=0;
 char empty=0;
 pid_t pid;
 
+       d2("lockdevice(), locked=%d\n",locked);
+       if (locked) return (++locked);
        for (;;) {
                if (fd!=-1) lockclose(fd);
 recheck:
@@ -443,31 +728,45 @@ isempty:
                empty=0;
                errno=0;
                if (kill(pid,0) && errno!=ESRCH && errno!=EPERM)
-                       error("Error during checking consciousness of PID %d: %m",pid);
-               if (errno!=ESRCH) continue;
+                       error("^Error during checking consciousness of PID %d",pid);
+               if (errno!=ESRCH) {
+                       if (attempt) return(0);
+                       continue;
+                       }
                error(".Lockfile \"%s\" is stale (PID %d), removing it",lockreal,pid);
 remove:
                lockclose(fd);
                if (unlink(lockreal))
-                       error("Error removing foreign lockfile \"%s\": %m",lockreal);
+                       error("^Error removing foreign lockfile \"%s\"",lockreal);
                break;
                }
        errno=0;
        if ((fd=open(lockreal,O_WRONLY|O_CREAT|O_EXCL,0644))==-1) {
                if (errno==EEXIST) goto recheck;
-               error("!Error creating lockfile \"%s\": %m",lockreal);
+               error("^!Error creating lockfile \"%s\"",lockreal);
                }
        locked=1;
        got=VARPRINTF(buf,"%010d\n",getpid()); assert(got==11);
        if (write(fd,buf,got)!=got)
-               error("!Error writing data to lockfile \"%s\": %m",lockreal);
+               error("^!Error writing data to lockfile \"%s\"",lockreal);
        lockclose(fd);
+       return((locked=1));
 }
 
 static char wasalarm=0;
-static void sigalarm(int signo)
+static void sigalarm(int signo);
+
+static void setalarm(void)
 {
        signal(SIGALRM,(RETSIGTYPE (*)(int))sigalarm);
+#ifdef HAVE_SIGINTERRUPT
+       siginterrupt(SIGALRM,1);
+#endif
+}
+
+static void sigalarm(int signo)
+{
+       setalarm();
        wasalarm=1;
        if (verbose>=1) error("Timed out");
 }
@@ -477,7 +776,7 @@ static void blocking(char yes)
 static char state=-1;
        if (state==yes) return;
        if (fcntl(devfd,F_SETFL,(yes?0:O_NONBLOCK)))
-               error("!fcntl() on device for %sblocking mode: %m",(yes?"":"non-"));
+               error("^!fcntl() on device for %sblocking mode",(yes?"":"non-"));
        state=yes;
 }
 
@@ -488,40 +787,94 @@ static size_t catchdatal,catchdatasiz;
 static void catched(const char *end)
 {
 size_t len;
-void *p;
+const char *p1,*p2;
 
        if (!record) return;
        assert(end>=record);
-       if ((p=memchr(record,'\n',end-record))) end=p;
-       if ((len=end-record)) {
+       p1=memchr(record,'\n',end-record);
+       p2=memchr(record,'\r',end-record);
+       if (!p1 || (p1 && p2 && p2<p1)) p1=p2;
+       if ((len=(p1?p1:end)-record)) {
+               if (!catchdata)
+                       chk(catchdata=malloc((catchdatasiz=LINE_MAX)));
                if (catchdatal+len>catchdatasiz)
                        chk(catchdata=realloc(catchdata,
-                               (catchdatasiz=MAX(LINE_MAX,(catchdatal+len)*2))));
+                               (catchdatasiz=(catchdatal+len)*2)));
                memcpy(catchdata+catchdatal,record,len);
                catchdatal+=len;
                }
-       record=(p?NULL:end);
+       record=(p1?NULL:end);
        assert(catchdatal<=catchdatasiz);
 }
 
 static int retrycnt=0;
 static void retrying(void)
 {
-       if (++retrycnt>maxretryn) error("!Maximum command retry count (%d) exceeded",maxretryn);
-       if (verbose>=2) error(".Retrying phase, %d out of %d..",retrycnt,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);
+}
+
+static char *reform(const char *s,int slot)
+{
+static struct formslot {
+       char *s;
+       size_t l;
+       } arr[3];
+char c,*d;
+struct formslot *fs;
+
+       assert(slot>=0 && slot<NELEM(arr));
+       if (!s) return("<unset>");
+       if (!(fs=&arr[slot])->s)
+               chk(fs->s=malloc(fs->l=LINE_MAX));
+       d=fs->s;
+       for (*d++='"';(c=*s);s++) {
+               if (d>=fs->s+fs->l-10) {
+off_t o=d-fs->s;
+                       chk(fs->s=realloc(fs->s,(fs->l=(fs->l?fs->l*2:LINE_MAX))));
+                       d=fs->s+o;
+                       }
+               if (c!='\\' && c!='"' && isprint(c)) { *d++=c; continue; }
+               *d++='\\';
+               switch (c) {
+                       case '\\': case '"': *d++=c; break;
+                       case '\n': *d++='n'; break;
+                       case '\r': *d++='r'; break;
+                       case '\032': *d++='Z'; break;
+                       case '\033': *d++='e'; break;
+                       default:
+                               d+=sprintf(d,"x%02X",(unsigned char)c);
+                               break;
+                       }
+               }
+       *d++='"'; *d='\0';
+       return(fs->s);
 }
 
+static char *devcmd(const char *term,const char *catch,const char *send,...) ATTR_PRINTFORMAT(3,4);
 static char *devcmd(const char *term,const char *catch,const char *send,...)
 {
-size_t l,bufl,terml,catchl,fragl,offs;
-char buf[LINE_MAX];
+size_t l,bufl2,terml,catchl,fragl,offs;
+static char buf[LINE_MAX];
+static size_t bufl;
 ssize_t got;
 char *hit,*s;
 va_list ap;
-char errout;
+char errout,extend,convcr;
+long alarmtime;
+const char *osend;
+static const char emptystring[]="";
 
        if (!term) term="\nOK\n";
+       convcr=!strchr(term,'\r');
+       if (!strcmp(send," ")) send=NULL; /* GCC formatstring-check workaround */
+       if (!(osend=send)) send="";
        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, convcr=%d, osend=%p, bufl=%d, buf: %s\n",
+               alarmtime,errout,extend,convcr,osend,bufl,reform(buf,0));
        if (0) {
 err:
                alarm(0);
@@ -529,26 +882,31 @@ err:
                retrying();
                }
        catchdatal=0;
-       va_start(ap,send);
-       l=VARVPRINTF(buf,send,ap); bufl=l+1;
-       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\")",buf,term,catch);
-       buf[l]='\r'; buf[l+1]='\n';
-       for (offs=0,got=0;offs<bufl;offs++) {
-               alarm(MAXSENDTIME);
-               usleep((offs?chartimen:cmdtimen)*1000);
-               if (!offs && tcflush(devfd,TCIOFLUSH))
-                       error("Error flushing I/O queue of device: %m");
-               if (write(devfd,buf+offs,1)!=1) break;
-               got++;
-               if (tcdrain(devfd))
-                       error("Error forcing output of char %d of cmd \"%s\": %m",offs,buf);
-               }
-       alarm(0);
-       if (got!=bufl) {
-               error("Wrote only %d of %d bytes of command: %m",got,bufl);
-               goto err;
+       if (osend) {
+               bufl=0;
+               d1("Resetting bufl.\n");
+               va_start(ap,send);
+               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 (osend) buf[l]='\r';
+               for (offs=0,got=0;offs<bufl;offs++) {
+                       alarm(MAXSENDTIME);
+                       usleep((offs?chartimen:cmdtimen)*1000);
+                       if (!offs && tcflush(devfd,TCIOFLUSH))
+                               error("^Error flushing I/O queue of device");
+                       if (write(devfd,buf+offs,1)!=1) break;
+                       got++;
+                       if (tcdrain(devfd))
+                               error("^Error forcing output of char at pos %d of cmd %s",offs,reform(buf,0));
+                       }
+               alarm(0);
+               if (got!=bufl) {
+                       error("^Wrote only %d of %d bytes of command",got,bufl);
+                       goto err;
+                       }
                }
 
        if (!(terml=strlen(term))) {
@@ -561,10 +919,14 @@ err:
                }
        else fragl=terml;
        fragl=MAX(fragl,MAX(strlen(ERROR_SUBSTR1),strlen(ERROR_SUBSTR2)));
-       bufl=0;
        record=NULL;
        wasalarm=0;
-       alarm(readtimen);
+       alarm(alarmtime);
+       if (!osend) {
+               got=bufl;
+               bufl=0;
+               goto skipread;
+               }
        for (;;) {
                blocking(0);
                errno=0;
@@ -575,45 +937,53 @@ err:
                        got=read(devfd,buf+bufl,1);
                        }
                if (got<=0) {
-                       if (wasalarm) error("!Maximum response timeout (%ds) exceeded",readtimen);
-                       error("Couldn't read device data (ret=%d): %m",got);
+                       if (wasalarm) error("Maximum response timeout (%lds) exceeded",alarmtime);
+                       else error("^Couldn't read device data (ret=%d)",got);
                        goto err;
                        }
+skipread:
+               bufl2=bufl+got;
+               buf[bufl2]='\0';
                s=buf+bufl;
-               bufl+=got;
-               /* FIXME: '\0' conversion */
-               while (buf+bufl>s && (s=memchr(s,'\r',buf+bufl-s))) *s='\n';
+               while (buf+bufl2>s && (s=memchr(s,'\0',buf+bufl2-s))) *s++=REPL_NULLCHAR;
+               if (verbose>=2)
+                       error("\nGot chunk of data from device: %s",reform(buf+bufl,0));
+               if (convcr) {
+                       s=buf+bufl;
+                       while (buf+bufl2>s && (s=memchr(s,'\r',buf+bufl2-s))) *s++='\n';
+                       }
+               bufl=bufl2;
                catched(buf+bufl); assert(!record || record==buf+bufl);
-               assert(bufl<sizeof(buf));
-               if (bufl>=fragl) {
-                       buf[bufl]='\0';
-                       assert(strlen(buf)==bufl);
-                       /* d3(">%s|%s<\n",buf,term); */
-                       if (strstr(buf,ERROR_SUBSTR1) || strstr(buf,ERROR_SUBSTR2)) {
-                               error("Found ERROR response on command \"%s\"",send);
-                               goto err;
-                               }
-                       if (catch && bufl>=catchl && (hit=strstr(buf,catch))) {
-                               record=hit+catchl;
-                               catched(buf+bufl); assert(!record || record==buf+bufl);
-                               }
-                       if (         bufl>= terml && (hit=strstr(buf,term))) break;
-                       memmove(buf,buf+bufl-(fragl-1),fragl-1);
-                       bufl=fragl-1;
-                       if (record) record=buf+bufl;
+               assert(bufl<sizeof(buf)-1);
+               buf[bufl]='\0';
+               assert(strlen(buf)==bufl);
+               /* d3(">%s|%s<\n",buf,term); */
+               if (strstr(buf,ERROR_SUBSTR1) || strstr(buf,ERROR_SUBSTR2)) {
+                       error("Found ERROR response on command %s",reform(send,0));
+                       goto err;
+                       }
+               if (catch && bufl>=catchl && (hit=strstr(buf,catch))) {
+                       record=hit+catchl;
+                       catched(buf+bufl); assert(!record || record==buf+bufl);
                        }
+               if (         bufl>= terml && (hit=strstr(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+bufl;
                }
        alarm(0);
        if (!catchdatal) {
-               if (!catch) return(NULL);
-               error("Data requested on command \"%s\" but no found after term \"%s\"",send,term);
+               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);
-       record=buf;
-       buf[0]='\0';
-       catched(buf+1);
-       if (verbose>=2) error(".Returning data \"%s\" for cmd \"%s\"",catchdata,send);
+       record=emptystring;
+       catched(record+1);
+       if (verbose>=2) error(".Returning data %s for cmd %s",reform(catchdata,0),reform(send,1));
        return(catchdata);
 }
 
@@ -647,7 +1017,7 @@ static inline char tohex(unsigned char x)
                  return(x-10+'A');
 }
 
-static void textconv(char *d,unsigned char *s,size_t len)
+static inline void textconv(char *d,unsigned char *s,size_t len)
 {
        while (len--) {
                *d++=tohex(*s>>4U);
@@ -667,10 +1037,12 @@ unsigned char bin[2+(MAXNUMLEN+1)/2];
        s=devcmd(NULL,"\n+CSCA:","\r\nAT+CSCA?");
        while (isspace(*s)) s++;
        if (!*s || !strcmp(s,"EMPTY"))
-               error("!No SMS set in A1 found");
-       if (verbose>=1) error("\nFound default SMSC in A1: %s",s);
+               error("!No SMS 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)
+               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);
@@ -681,14 +1053,13 @@ unsigned char bin[2+(MAXNUMLEN+1)/2];
        if (l==ADDR_NAT || s[1]=='+') s++;
        else *s='+';
        *e='\0';
+       if (verbose>=2) error("\nDecoded SMSC address: %s",s);
+       if (!NEED_PDUSMSC()) return;
        chk(finalsmsc=strdup(s));
-       if (verbose>=2) error("\nDecoded SMSC address: %s",finalsmsc);
        bin[0]=1+(prepaddr(bin,finalsmsc)+1)/2;
        textconv(pdusmsc,bin,bin[0]+1);
 }
 
-static char *pdudata;
-
 static inline unsigned char charconv(char c,size_t offs)
 {
        switch (c) {
@@ -706,12 +1077,106 @@ static inline unsigned char charconv(char c,size_t offs)
 #endif
 }
 
+/* Logo format shamelessly stolen from GNokii-0.3.0: http://www.gnokii.org/
+ * Beware - Nokia Smart Messaging specification 1.0.0 and 2.0.0 is incompatible
+ * with Nokia current product line implementation
+ * http://www.forum.nokia.com/developers/smartmsg/download/ssm2_0_0.pdf
+ */
+
+static char *pdudata;
+static char hexdata[140*2+1];
+
+static inline void logoread(void)
+{
+FILE *f;
+char buf[32+140*8+1];
+unsigned char bin[140]={
+       0x06, /* UDH length */
+       0x05, /* IEI */
+       0x04, /* IEDL */
+       0x15, 0x83, /* dest port (group gfx) */
+       0x00, 0x00  /* src port (unused) */
+       };
+size_t got,r,w;
+ssize_t chars,bits;
+char gsmnetf[10];
+int sizex,sizey,bit;
+
+#define WORD(n) (((unsigned char)buf[(n)])|(((unsigned char)buf[(n)+1])<<8))
+
+       if (!(f=fopen(logoname,"rb")))
+               error("^!Cannot open logo file \"%s\" for r/o",logoname);
+       got=fread(buf,1,sizeof(buf),f);
+            if (got>=20 && !memcmp(buf,"NOL",4)) {
+               VARPRINTF2(gsmnetf,"%03.3u%02.2u",WORD(6),WORD(8));
+               assert(strlen(gsmnetf)==5);
+               r=10;
+               if (verbose>=1) error(".Reading NOL file \"%s\", GSMnet \"%s\", word@4=%d..",
+                       logoname,gsmnetf,WORD(4));
+               }
+       else if (got>=16 && !memcmp(buf,"NGG",4)) {
+               r=6;
+               if (verbose>=1) error(".Reading NGG file \"%s\", word@4=%d..",
+                       logoname,WORD(4));
+               }
+       else error("!Unknown file format of logo file \"%s\"",logoname);
+       if (gsmnet && !strtrycasecmp(gsmnet,WORD_NET)) {
+               if (!*gsmnetf) error("!NOL network code detection requested but NOL file not loaded, please specify network code");
+               gsmnet=gsmnetf;
+               }
+       if (!gsmnet || !strtrycasecmp(gsmnet,WORD_GROUP) || !*gsmnet) {
+               error("\nSending logo as: group graphics");
+               gsmnet=NULL;
+               }
+       else {
+               error("\nSending logo as: operator logo for \"%s\"",gsmnet);
+               bin[4]=0x82; /* dest port 0x1582 */
+               }
+       
+       sizex=WORD(r); sizey=WORD(r+2);
+       if (verbose>=2) error(".Magic words: @+4=%d, @+6=%d, @+8=%d",
+                       WORD(r+4),WORD(r+6),WORD(r+8));
+       r+=10;
+       if (sizex<1 || sizex>255
+        || sizey<1 || sizey>255) error("!Invalid size: %dx%d",sizex,sizey);
+       chars=((bits=sizex*sizey)+7)/8;
+       if (r+bits>got) error("!Logo file \"%s\" too short - actual=%d, need(%dx%d)=%d",
+               logoname,got,sizex,sizey,r+chars);
+       else if (r+bits<got)
+               if (verbose>=1) error("Ignoring trailing garbage in \"%s\", used only %d bytes",logoname,r+bits);
+       if ((got=(7+(gsmnet?3:0)+4+chars))>140)
+               error("!SMS size would be %d bytes but 140 is maximum",got);
+       w=7;
+       if (gsmnet) {
+               bin[w++]=((gsmnet[1]&0x0F)<<4)|(gsmnet[0]&0x0F);
+               bin[w++]=0xF0                 |(gsmnet[2]&0x0F);
+               bin[w++]=((gsmnet[4]&0x0F)<<4)|(gsmnet[3]&0x0F);
+               }
+       bin[w++]=0x00; /* RFU by Nokia */
+       bin[w++]=sizex; bin[w++]=sizey;
+       bin[w++]=0x01; /* one B/W plane */
+       while (chars--) {
+               bin[w]=0;
+               for (bit=0x80;(bits>0) && (bit>0);bits--,bit>>=1) {
+                       if (buf[r]!='0' && buf[r]!='1')
+                               error("!Invalid character (neither '0' nor '1')in logo file \"%s\" at offset 0x%X",
+                                       logoname,r);
+                       if (buf[r++]=='1') bin[w]|=bit;
+                       }
+               w++;
+               }
+       assert(chars==-1); assert(bits==0); assert(w==got); assert(w<=140);
+       textconv(hexdata,bin,w);
+       if (verbose>=2) error("\nWill send hexdata: %s",hexdata);
+#undef WORD
+}
+
 static inline void genpdu(void)
 {
 static unsigned char pdu[64+MAXNUMLEN/2+(MAXBODYLEN*7)/8];
 unsigned char *d=pdu;
 int i;
-char inb=0,outb=0,xb;
+char inb=0,outb=0,xb,*bodyr;
 unsigned char inreg;
 size_t offs=0;
 
@@ -724,14 +1189,15 @@ size_t offs=0;
        *d++=PDU_VP;
        if (bodylen>MAXBODYLEN) {
                error("Body too large (%d>%d), cut",bodylen,MAXBODYLEN);
-               bodylen=MAXBODYLEN;
+               body[(bodylen=MAXBODYLEN)]='\0';
                }
+       bodyr=body;
        *d=bodylen;
        assert(d<pdu+sizeof(pdu));
        while (bodylen || inb) {
                if (!inb) {
                        assert(bodylen>0); assert(!!*body);
-                       inreg=charconv(*body++,offs++);
+                       inreg=charconv(*bodyr++,offs++);
                        bodylen--;
                        inb=7;
                        }
@@ -740,7 +1206,9 @@ size_t offs=0;
                        outb=8;
                        }
                xb=MIN(inb,outb);
+#if 0
                d4("inb=%d,outb=%d,xb=%d\n",inb,outb,xb);
+#endif
                *d|=((inreg>>(unsigned)(7-inb))&((1<<xb)-1))<<(unsigned)(8-outb);
                inb-=xb; outb-=xb;
                }
@@ -750,6 +1218,267 @@ size_t offs=0;
        textconv(pdudata,pdu,d-pdu);
 }
 
+static inline void preparebody(void)
+{
+FILE *fin;
+char *finame;
+
+       if (body && readbody) {
+               finame=body;
+               body=NULL;
+               }
+       else {
+               finame=NULL;
+               fin=stdin;
+               }
+       if (body) return;
+       readbody=0;
+       if (!finame) {
+               if (verbose>=1)
+                       error("\nPlease enter the SMS text body, end with EOF (ctrl-D):");
+               }
+       else {
+               if (!(fin=fopen(finame,"rt")))
+                       error("^!Can't open data file \"%s\" for r/o",finame);
+               }
+       chk(body=malloc(BODYLOAD));
+       bodylen=fread(body,1,BODYLOAD,fin);
+       if (bodylen==-1)
+               error("^!Error reading stream \"%s\"",(finame?finame:"<stdin>"));
+       if (finame) {
+               chkfclose(fin,finame);
+               free(finame);
+               }
+}
+
+static int datawait(char immed)
+{
+int i;
+#ifdef HAVE_POLL
+struct pollfd ufd;
+#else /* HAVE_POLL */
+fd_set rfds,xfds;
+#endif /* HAVE_POLL */
+
+       assert(devfd>=0);
+retry:
+       errno=0;
+
+#ifdef HAVE_POLL
+       ufd.fd=devfd;
+       ufd.events=POLLIN;
+       ufd.revents=0;
+       i=poll(&ufd,1,(immed?0:-1));
+#else /* HAVE_POLL */
+#ifdef HAVE_FD_SETSIZE
+       if (devfd>=FD_SETSIZE)
+               error("!Device file descriptor %d can't fit in select() FD_SETSIZE (%d)",
+                       devfd,FD_SETSIZE);
+#endif /* HAVE_FD_SETSIZE */
+       FD_ZERO(&rfds); FD_SET(devfd,&rfds);
+       FD_ZERO(&xfds); FD_SET(devfd,&xfds);
+       i=select(devfd+1,&rfds,NULL,&xfds,NULL);
+#endif /* HAVE_POLL */
+       if (immed && i==0) return(0);
+       if (i!=1)
+               error("^Failed (retval %d) while waiting for data, ignoring",i);
+
+#ifdef HAVE_POLL
+       if (ufd.revents&(POLLERR|POLLHUP))
+#else /* HAVE_POLL */
+       if (FD_ISSET(devfd,&xfds))
+#endif /* HAVE_POLL */
+               error("^Error while waiting for data, ignoring");
+
+#ifdef HAVE_POLL
+       if (!(ufd.revents&POLLIN))
+#else /* HAVE_POLL */
+       if (!(FD_ISSET(devfd,&rfds)))
+#endif /* HAVE_POLL */
+               {
+               error("^No data input after waited for data, retrying");
+               goto retry;
+               }
+       return(1);
+}
+
+static char *check_format(const char *fmt,const char *string)
+{
+static char err[LINE_MAX],sub[50];
+char *subp,cf,cs;
+const char *sf,*ss;
+
+       for (sf=fmt,ss=string;(cf=*sf) && (cs=*ss);sf++,ss++) {
+               subp=NULL;
+               switch (cf) {
+                       case '?':
+                               break;
+                       case '9':
+                               if (isdigit(cs)) break;
+                               subp="digit";
+                               break;
+                       case '+':
+                               if (cs=='+' || cs=='-') break;
+                               subp="+/- sign";
+                               break;
+                       default:
+                               if (cf==cs) break;
+                               VARPRINTF(sub,"'%c'",cf); subp=sub;
+                       }
+               if (!subp) continue;
+               VARPRINTF5(err,"Expected %s, found '%c' at pos %d of string [%s], formatstring [%s]",
+                       subp,cs,ss-string,string,fmt);
+               return(err);
+               }
+       if (*sf) {
+               VARPRINTF2(err,"String too short for format, string [%s], formatstring [%s]",
+                       string,fmt);
+               return(err);
+               }
+       if (*ss) {
+               VARPRINTF2(err,"Trailing garbage in string [%s], formatstring [%s]",
+                       string,fmt);
+               return(err);
+               }
+       return(NULL);
+}
+
+static char *receive_number;
+static time_t receive_time;
+
+/* +CMT: "+420602431329",,"99/10/25,03:21:03-00" */
+static int receive_headerparse(char *buf)
+{
+char *s,*s1,*err;
+struct tm tm;
+static const struct {
+       off_t strpos;
+       off_t tmpos;
+       int min,max;
+       const char *name;
+       } timeparse[]={
+#define TP_ENT(a,b,c,d,e) { a,offsetof(struct tm,b),c,d,e }
+               TP_ENT( 3,tm_year,0,99,"year"),
+               TP_ENT( 6,tm_mon ,1,12,"month"),
+               TP_ENT( 9,tm_mday,1,31,"day of month"),
+               TP_ENT(12,tm_hour,0,23,"hour"),
+               TP_ENT(15,tm_min ,0,59,"minute"),
+               TP_ENT(18,tm_sec ,0,59,"second"),
+               /* Time zone ignored */
+               };
+int i,val;
+
+#define DIGIT2(s) (((s)[0]-'0')*10+((s)[1]-'0'))
+
+       for (s=buf;*s==' ';s++);
+       if (*s++!='"') {
+               error("Cannot find initial '\"' in CMT header: %s",buf);
+               return(0);
+               }
+       for (s1=s;*s && *s!='"';s++);
+       if (!*s) {
+               error("Only one '\"' found in CMT header: %s",buf);
+               return(0);
+               }
+       free(receive_number);
+       chk(receive_number=malloc(s-s1+1));
+       memcpy(receive_number,s1,s-s1); receive_number[s-s1]='\0';
+       s++;
+       if ((err=check_phone(receive_number)) ||
+           (err=check_format(",,\"99/99/99,99:99:99+99\"",s))) {
+               error("%s in CMT header: %s",err,buf);
+               return(0);
+               }
+       memset(&tm,0,sizeof(tm)); /* may be redundant */
+       for (i=0;i<NELEM(timeparse);i++) {
+               val=DIGIT2(s+timeparse[i].strpos);
+               if (val<timeparse[i].min || val>timeparse[i].max) {
+                       error("Weird value of %s, is %d but expected %d..%d, setting to %d",
+                               timeparse[i].name,val,timeparse[i].min,timeparse[i].max,timeparse[i].min);
+                       val=timeparse[i].min;
+                       }
+               *(int *)(((char *)&tm)+timeparse[i].tmpos)=val;
+               }
+       if (tm.tm_year<70) tm.tm_year+=100;
+       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);
+       if ((receive_time=mktime(&tm))==-1) {
+               error("^mktime(3) failed for %s",s+2);
+               return(0);
+               }
+       return(1);
+#undef DIGIT2
+}
+
+static void receive_accept(char *bodyline)
+{
+char *buf,*s,*s1,*s2,*s3;
+pid_t pid;
+char tbuf[32];
+int i;
+FILE *f;
+
+       d2("receive_accept: %s\n",bodyline);
+#if RECEIVE_TEST
+       pid=0;
+#else
+       pid=fork();
+#endif
+       if (pid>0) return; /* parent context */
+       if (pid==-1) {
+               error("Can't fork(2), process spawning may block receive");
+               }
+       else { /* child process */
+               dis_cleanup=1;
+               }
+       for (s=body;*s;) {
+               s1=s;
+               do {
+                       s1=strchr(s1+(s1!=s),'%');
+                       } while (s1 && s1[1]!='p' && s1[1]!='T' && s1[1]!='t');
+               if (!s1) {
+                       pushargstack_one(s,0);
+                       break;
+                       }
+               *s1='\0';
+               pushargstack_one(s,0);
+               *s1++='%';
+               s=s1;
+               switch (*s++) {
+                       case 'p':
+                               pushargstack_one(receive_number,0);
+                               break;
+                       case 'T':
+                               VARPRINTF(tbuf,"%ld",receive_time);
+                               pushargstack_one(tbuf,0);
+                               break;
+                       case 't':
+                               if (!(s2=ctime(&receive_time))) {
+                                       error("Failing ctime(3), ignoring substitution");
+                                       break;
+                                       }
+                               if ((s3=strchr(s2,'\n'))) *s3='\0';
+                               pushargstack_one(s2,0);
+                               break;
+                       default: assert(0);
+                       }
+               }
+       buf=glueargstack(NULL,NULL); assert(buf);
+       if (!(f=popen(buf,"w"))) {
+               error("^Failing spawn of receive command: %s",buf);
+               goto err;
+               }
+       if (fputs(bodyline,f)<0 || putc('\n',f)!='\n')
+               error("^Failing write to child receive command: %s",buf);
+       if ((i=pclose(f)))
+               error("^Spawned receive command failure (code %d): %s",i,buf);
+err:
+       free(buf);
+       if (pid==-1) return;
+       exit(EXIT_SUCCESS); /* cleanup() has been disabled */
+}
+
 static struct {
        char **sp;
        long *ip;
@@ -759,21 +1488,39 @@ static struct {
                { &readtime,&readtimen,"readtime" },
                { &chartime,&chartimen,"chartime" },
                { &cmdtime ,&cmdtimen ,"cmdtime"  },
+               { &baud    ,&baudn    ,"baud"     },
        };
 
 int main(int argc,char **argv)
 {
-char *s,*finame;
-FILE *fin;
+char *s;
 int i;
 unsigned fatal=0;
+speed_t portbaud;
+enum modenum argsmode;
 
        if ((s=strrchr((pname=*argv),'/'))) pname=s+1;
+#ifdef HAVE_ATEXIT
        atexit(cleanup);
+#else
+       error("atexit(3) not available at compilation time, device cleanup may be missed");
+#endif
        signal(SIGTERM,(RETSIGTYPE (*)(int))cleanup);
        signal(SIGQUIT,(RETSIGTYPE (*)(int))cleanup);
        signal(SIGINT ,(RETSIGTYPE (*)(int))cleanup);
        signal(SIGHUP ,(RETSIGTYPE (*)(int))cleanup);
+       assert(mode==MODE_UNKNOWN);
+       for (i=0;i<NELEM(longopts);i++) {
+               if (longopts[i].val<MODE_FIRST) break;
+               if (!strstr(pname,longopts[i].name)) continue;
+               if (mode==MODE_UNKNOWN) {
+                       mode=longopts[i].val;
+                       continue;
+                       }
+               mode=MODE_UNKNOWN;
+               break;
+               }
+       argsmode=mode;
        processargs(argc,argv,"<command-line>");
        if ((s=getenv("HOME"))) {
 size_t l=strlen(s);
@@ -785,48 +1532,38 @@ char *buf=malloc(l+50);
                free(buf);
                }
        readfile(CONFIG_MAIN,1);
+       if (verbose>=1) {
+               if (argsmode)
+                       error(".Detected mode \"%s\" from my program name \"%s\"",MODE_NAME(argsmode),pname);
+               else
+                       error(".Automatic mode detection unsuccessul for my progam name \"%s\"",pname);
+               }
 
-       for (i=0;i<NELEM(nullcheck);i++)
-               if (!*nullcheck[i].var) {
-                       error("Missing parameter \"%s\"",nullcheck[i].name);
-                       fatal++;
-                       }
+       if (!mode)
+               error("!Operation mode unset, use --send or similiar command, see help (-h)");
+       error(".Running program in mode \"%s\"",MODE_NAME(mode));
+       switch (mode) {
+
+               case MODE_SEND:           /* FALLTHRU */
+               case MODE_SEND_MOBILDOCK: cmdline_send     (); break;
+               case MODE_LOGO_SEND:      cmdline_logo_send(); break;
+               case MODE_RECEIVE:        cmdline_receive  (); break;
+               default: assert(0);
+               }
+       cmdline_done();
+       for (i=0;i<NELEM(nullcheck);i++) {
+const struct nullcheck *n=nullcheck+i;
+
+               if (*n->var) continue;
+               if (n->reqd && !(MODE_BIT(mode)&n->reqd)) continue;
+               error("Missing parameter \"%s\"",n->name);
+               fatal++;
+               }
        if (fatal) error("!Previous error%s considered unrecoverable",(fatal==1?"":"s"));
-       for (i=0;i<NELEM(emptycheck);i++)
-               if (*emptycheck[i] && !**emptycheck[i]) {
-                       free(*emptycheck[i]);
-                            *emptycheck[i]=NULL;
-                       }
+       emptyclean();
        if (!logname) logname=DEF_LOGNAME;
        if (!lockfile) lockfile=DEF_LOCKFILE;
        if (!device) device=DEF_DEVICE;
-       if (body && readbody) {
-               finame=body;
-               body=NULL;
-               }
-       else {
-               finame=NULL;
-               fin=stdin;
-               }
-       if (!body) {
-               readbody=0;
-               if (!finame) {
-                       if (verbose>=1)
-                               error("\nPlease enter the SMS text body, end with EOF (ctrl-D):");
-                       }
-               else {
-                       if (!(fin=fopen(finame,"rt")))
-                               error("!Can't open data file \"%s\" for r/o: %m",finame);
-                       }
-               chk(body=malloc(BODYLOAD));
-               bodylen=fread(body,1,BODYLOAD,fin);
-               if (bodylen==-1)
-                       error("!Error reading stream \"%s\": %m",(finame?finame:"<stdin>"));
-               if (finame) {
-                       chkfclose(fin,finame);
-                       free(finame);
-                       }
-               }
 
        for (i=0;i<NELEM(numarg);i++) {
 char *serr;
@@ -836,6 +1573,9 @@ char *serr;
                        error("!Number parse error for parameter \"%s\" of \"%s\" at: %s",
                                numarg[i].msg,*numarg[i].sp,serr);
                }
+       if (readtimen==-1)
+               readtimen=(mode==MODE_SEND_MOBILDOCK?DEF_READTIME_MOBILDOCK:DEF_READTIME);
+       if (mode==MODE_SEND_MOBILDOCK) mode=MODE_SEND;
 
        if (!strchr(device,'/')) {
 size_t l=strlen(device);
@@ -859,54 +1599,139 @@ size_t l=strlen(device);
        
        if (*logname) {
                if (!(logf=fopen(logname,"a")))
-                       error("!Error opening log \"%s\" for append: %m",logname);
+                       error("^!Error opening log \"%s\" for append",logname);
                logmsg("Starting up: " PACKAGE " " VERSION);
                }
-       genpdu();
+
+       switch (mode) {
+               case MODE_SEND:
+                       preparebody();
+                       genpdu();
+                       readbody=0;
+                       break;
+               case MODE_LOGO_SEND:
+                       logoread();
+                       break;
+               case MODE_RECEIVE: break;
+               default: assert(0);
+               }
+       if (readbody)
+               error("Warning: -f / --file is forbidden with mode \"%s\"",MODE_NAME(mode));
+
+       switch (baudn) {
+               case  2400: portbaud= B2400; break;
+               case  4800: portbaud= B4800; break;
+               case  9600: portbaud= B9600; break;
+               case 19200: portbaud=B19200; break;
+               case 38400: portbaud=B38400; break;
+               case 57600: portbaud=B57600; break;
+               default:
+                       error("!Specified baudrate %ld is not supported",baudn);
+               }
+       if (verbose>=2) error(".Will use baudrate %ld with hexval 0x%X",baudn,portbaud);
                
        if (lockfile && *lockfile && VARPRINTF(lockreal,lockfile,devicename)>0) {
 time_t start,end;
                if (verbose>=1) error(".Locking device \"%s\" by \"%s\"..",device,lockreal);
                time(&start);
-               lockdevice();
+               lockdevice(0);
                time(&end);
                if ((end-=start)>LOCKREPORT)
-                       logmsg("Device lock succeeded after %d seconds",end);
+                       logmsg("Device lock succeeded after %ld seconds",(long)end);
                }
        if (verbose>=1) error(".Opening device \"%s\"..",device);
        if ((devfd=open(device,O_RDWR|O_NDELAY))<0)
-               error("!Cannot open device \"%s\" for rw-access: %m",device);
+               error("^!Cannot open device \"%s\" for r/w access",device);
        
        if (tcgetattr(devfd,&restios))
-               error("Unable to get termios settings: %m");
+               error("^Unable to get termios settings");
        else {
                restios.c_cflag=(restios.c_cflag&~(CBAUD|CBAUDEX))|B0|HUPCL;
                restios_yes=1;
                }
        tios.c_iflag=IGNBRK|IGNPAR|IXON|IXOFF;
        tios.c_oflag=0;
-       tios.c_cflag=CS8|CREAD|CLOCAL|B19200|HUPCL;
+       tios.c_cflag=CS8|CREAD|CLOCAL|HUPCL|portbaud;
        tios.c_lflag=IEXTEN|NOFLSH;
        memset(tios.c_cc,_POSIX_VDISABLE,sizeof(tios.c_cc));
        tios.c_cc[VTIME]=0;
        tios.c_cc[VMIN ]=1;
-           cfsetispeed(&tios,B19200);
-       if (cfsetospeed(&tios,B19200)|cfsetispeed(&tios,B19200))
-               error("Error setting termios baudrate on device: %m");
+           cfsetispeed(&tios,portbaud);
+       if (cfsetospeed(&tios,portbaud)|cfsetispeed(&tios,portbaud))
+               error("^Error setting termios baudrate on device");
        if (tcflush(devfd,TCIOFLUSH))
-               error("Error flushing termios (TCIOFLUSH) on device: %m");
+               error("^Error flushing termios (TCIOFLUSH) on device");
        if (tcsetattr(devfd,TCSANOW,&tios))
-               error("!Unable to set initial termios device settings: %m");
+               error("^!Unable to set initial termios device settings");
 
-       signal(SIGALRM,(RETSIGTYPE (*)(int))sigalarm);
-       do {
-               devcmd("",NULL,"\r\nAT\032");
+       setalarm();
+
+retryall:
+               devcmd("",NULL,"\r\nAT\033\032"); /* ESCAPE, CTRL-Z */
                devcmd(NULL,NULL,"\r\nAT");
                smscset();
-               devcmd(NULL,NULL,"\r\nAT+CMGF=0");
-               devcmd("\n> ",NULL,"\r\nAT+CMGS=%d",(strlen(pdusmsc)+strlen(pdudata))/2);
-               if (!(s=devcmd(NULL,"\n+CMGS:","!%s%s\032",pdusmsc,pdudata))) retrying();
-               } while (!s);
+               switch (mode) {
+                       case MODE_SEND:
+retrysendcmgf:
+                               if (!devcmd(NULL,NULL,"!\r\nAT+CMGF=0")) {
+                                       if (!devcmd(NULL,NULL,"!\r\nAT+CMGF=1"))
+                                               { retrying(); goto retrysendcmgf; }
+               /* CMGF=1 */
+                                       devcmd("\n> ",NULL,"\r\nAT+CMGS=\"%s\"",phone);
+                                       s=devcmd(NULL,"\n+CMGS:","!~%s\032",body);
+                                       }
+                               else {
+               /* CMGF=0 */
+                                       devcmd("\n> ",NULL,"\r\nAT+CMGS=%d",(strlen(pdusmsc)+strlen(pdudata))/2);
+                                       s=devcmd(NULL,"\n+CMGS:","!~%s%s\032",pdusmsc,pdudata);
+                                       }
+                               break;
+                       case MODE_LOGO_SEND:
+                               restore="\r\nAT+CSMP=17,,0,0";
+                               devcmd(NULL,NULL,"\r\nAT+CSMP=81,,0,245");
+                               devcmd("\n> ",NULL,"\r\nAT+CMGS=\"%s\"",phone);
+                               s=devcmd(NULL,"\n+CMGS:","!~%s\032",hexdata);
+                               break;
+                       case MODE_RECEIVE:
+                               devcmd(NULL,NULL,"\r\nAT+CMGF=1");
+                               restore="\r\nAT+CNMI=,0";
+                               devcmd(NULL,NULL,"\r\nAT+CNMI=,2");
+                               unlockdevice(0);
+                               /* Never bail-out when we got up to this point */
+                               if (maxretryn!=-1 && verbose>=1)
+                                       error(".Initialization successful, infinite retry count set");
+                               maxretryn=-1;
+#if RECEIVE_TEST
+               receive_headerparse(" \"+420602123456\",,\"99/10/25,03:21:03-00\"");
+               receive_accept("TESTBODY");
+               exit(EXIT_SUCCESS);
+#endif
+                               datawait(0);
+                               if (!lockdevice(1)) {
+                                       if (verbose>=1)
+                                               error(".Dialout detected, waiting for lock..");
+                                       lockdevice(0);
+                                       goto retryall;
+                                       }
+                               d1("Lock-device succeeded\n");
+                               do {
+                                       d1("Reading a message for us...\n");
+                                       if (!(s=devcmd("\r","+CMT:"," ")))
+                                               goto retryall;
+                                       if (!(i=receive_headerparse(s)))
+                                               error("Receive-header parsing failed on: %s",s);
+                                       if (!(s=devcmd("\r","\n"," ")))
+                                               goto retryall;
+                                       if (i) receive_accept(s);
+                                       if (!devcmd("\n",NULL," ")) /* eat last '\n' */
+                                               goto retryall;
+                                       } while (datawait(1));
+                               goto retryall;
+                               break;
+                       default: assert(0);
+                       }
+               if (!s) { retrying(); goto retryall; }
+
        while (isspace(*s)) s++;
        if (verbose>=1) error("\nMessage successfuly sent with MR: %s",s);
        devcmd(NULL,NULL,"\r\nAT");
diff --git a/need-declaration.m4 b/need-declaration.m4
deleted file mode 100644 (file)
index d5b7bc6..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-dnl See whether we need a declaration for a function.
-dnl GCC_NEED_DECLARATION(FUNCTION [, EXTRA-HEADER-FILES])
-AC_DEFUN(GCC_NEED_DECLARATION,
-[AC_MSG_CHECKING([whether $1 must be declared])
-AC_CACHE_VAL(gcc_cv_decl_needed_$1,
-[AC_TRY_COMPILE([
-#include <stdio.h>
-#ifdef HAVE_STRING_H
-#include <string.h>
-#else
-#ifdef HAVE_STRINGS_H
-#include <strings.h>
-#endif
-#endif
-#ifdef HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-$2],
-[char *(*pfn) = (char *(*)) $1],
-eval "gcc_cv_decl_needed_$1=no", eval "gcc_cv_decl_needed_$1=yes")])
-if eval "test \"`echo '$gcc_cv_decl_needed_'$1`\" = yes"; then
-  AC_MSG_RESULT(yes)
-  gcc_need_declarations="$gcc_need_declarations $1"
-  gcc_tr_decl=NEED_DECLARATION_`echo $1 | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
-  AC_DEFINE_UNQUOTED($gcc_tr_decl)
-else
-  AC_MSG_RESULT(no)
-fi
-])dnl
-
-dnl Check multiple functions to see whether each needs a declaration.
-dnl GCC_NEED_DECLARATIONS(FUNCTION... [, EXTRA-HEADER-FILES])
-AC_DEFUN(GCC_NEED_DECLARATIONS,
-[for ac_func in $1
-do
-GCC_NEED_DECLARATION($ac_func, $2)
-done
-]
-)
diff --git a/setup.h b/setup.h
index c057cb9..2ce6d6b 100644 (file)
--- a/setup.h
+++ b/setup.h
@@ -5,16 +5,21 @@
 #define CONFIG_MAIN "/etc/" PACKAGE "rc" /* global configuration file */
 #define CONFIG_HOME    "/." PACKAGE "rc" /* user-local cfg file,
                                             $HOME is prepended automatically */
-#define DEF_DEVICE "/dev/mobildock" /* --device default */
+#define DEF_DEVICE "/dev/mdsms" /* --device default */
 #define DEF_LOGNAME "" /* --log default */
 #ifndef DEF_LOCKFILE
 #define DEF_LOCKFILE "/var/lock/LCK..%s" /* --lockfile default */
 #endif
-#define DEF_MAXRETRY 10 /* --maxretry default */
-#define DEF_READTIME 60*60 /* --readtime default */
+#define DEF_BAUD 19200 /* --baud default */
+#define DEF_MAXRETRY 3 /* --maxretry default */
+#define DEF_READTIME_MOBILDOCK 60*60 /* --readtime default for MobilDock */
+#define DEF_READTIME 5 /* --readtime default */
+#define EXT_READTIME 10 /* --readtime multiply for long running commands */
 #define DEF_CHARTIME 10 /* --chartime default
                            (500ms per 80char=6.25ms recommended by Siemens */
 #define DEF_CMDTIME 500 /* --cmdtime default */
+#define WORD_NET "net" /* load as network logo */
+#define WORD_GROUP "group" /* load as group graphics */
 #define MAXCONFIG 4096 /* maximum allowed size of any configuration file */
 #define MAXCFGARGS 256 /* maximum allowed number of arguments in cfg file */
 #define MAXCFGLOOP 5 /* maximum number of recursive opens in cfg files */
@@ -37,3 +42,5 @@
 #define BODYLOAD 200 /* number of bytes to read when body not on command line */
 #define ERROR_SUBSTR1 "ERROR" /* first failure ind. substring in A1 response */
 #define ERROR_SUBSTR2 "\nError:" /* second such, this one from MobilDock */
+#define REPL_NULLCHAR ' ' /* char for replace when '\0' is read from device */
+#define RECEIVE_TEST 0 /* debugging test of --receive */
diff --git a/strdup.c b/strdup.c
deleted file mode 100644 (file)
index f67630f..0000000
--- a/strdup.c
+++ /dev/null
@@ -1,20 +0,0 @@
-#include "config.h"
-#ifndef lint
-static char rcsid[] ATTR_UNUSED = "$Id$";
-#endif
-
-/* This part of code is a public domain */
-
-#include <string.h>
-#include <stdlib.h>
-
-/* CONFORMING TO SVID 3, BSD 4.3 */
-
-char *strdup(const char *s)
-{
-size_t l;
-char *d;
-
-       if (!(d=malloc(l=strlen(s)+1))) return(NULL);
-       return memcpy(d,s,l);
-}