chat.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2004 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  */
00032 
00033 /*
00034  * $Log: chat.c,v $
00035  * Revision 1.8  2007/08/17 11:34:00  haraldkipp
00036  * Default timeout needs to be multiplied by 1000.
00037  *
00038  * Revision 1.7  2005/04/30 16:42:41  chaac
00039  * Fixed bug in handling of NUTDEBUG. Added include for cfg/os.h. If NUTDEBUG
00040  * is defined in NutConf, it will make effect where it is used.
00041  *
00042  * Revision 1.6  2004/03/16 16:48:27  haraldkipp
00043  * Added Jan Dubiec's H8/300 port.
00044  *
00045  * Revision 1.5  2004/03/08 11:12:59  haraldkipp
00046  * Debug output added.
00047  *
00048  * Revision 1.4  2004/01/15 08:02:35  drsung
00049  * Copyright added
00050  *
00051  */
00052 
00053 #include <cfg/os.h>
00054 #include <sys/timer.h>
00055 #include <dev/uart.h>
00056 
00057 #include <stdlib.h>
00058 #include <string.h>
00059 #include <io.h>
00060 
00061 #include <dev/chat.h>
00062 
00063 u_char *chat_report;
00064 
00065 #ifdef NUTDEBUG
00066 
00067 static FILE *__chat_trs;        
00068 static u_char __chat_trf;       
00077 void NutTraceChat(FILE * stream, u_char flags)
00078 {
00079     if (stream)
00080         __chat_trs = stream;
00081     if (__chat_trs) {
00082         static prog_char dbgfmt[] = "Chat trace flags=0x%02X\n";
00083         __chat_trf = flags;
00084         fprintf_P(__chat_trs, dbgfmt, flags);
00085     } else
00086         __chat_trf = 0;
00087 }
00088 
00089 #endif
00090 
00091 /*
00092  * Special version of strchr, handling escaped characters.
00093  */
00094 static char *strechr(CONST char *str, int c)
00095 {
00096     while (*str) {
00097         if (*str == '\\') {
00098             if (*++str)
00099                 str++;
00100         } else if (*str == c)
00101             return (char *) str;
00102         else
00103             str++;
00104     }
00105     return 0;
00106 }
00107 
00119 int NutChatExpectString(NUTCHAT * ci, char *str)
00120 {
00121     char ch;
00122     u_char m;
00123     u_char i;
00124     char *cp = str;
00125 
00126 #ifdef NUTDEBUG
00127     if (__chat_trf) {
00128         static prog_char dbgfmt[] = "Expect '%s', got '";
00129         fprintf_P(__chat_trs, dbgfmt, str);
00130     }
00131 #endif
00132 
00133     while (*cp) {
00134 
00135         /*
00136          * Read the next character. Return on timeout.
00137          */
00138         if (_read(ci->chat_fd, &ch, 1) != 1) {
00139 #ifdef NUTDEBUG
00140             if (__chat_trf) {
00141                 static prog_char dbgmsg[] = "' TIMEOUT\n";
00142                 fputs_P(dbgmsg, __chat_trs);
00143             }
00144 #endif
00145             return 3;
00146         }
00147 #ifdef NUTDEBUG
00148         if (__chat_trf) {
00149             if (ch > 31 && ch < 127) {
00150                 fputc(ch, __chat_trs);
00151             } else {
00152                 fprintf(__chat_trs, "\\x%02X", ch);
00153             }
00154         }
00155 #endif
00156         /*
00157          * If the character doesn't match the next expected one,
00158          * then restart from the beginning of the expected string.
00159          */
00160         if (ch != *cp) {
00161             cp = str;
00162         }
00163 
00164         /*
00165          * If the character matched, advance the pointer into
00166          * the expected string.
00167          */
00168         if (ch == *cp) {
00169             cp++;
00170         }
00171 
00172         /*
00173          * Check for abort strings.
00174          */
00175         for (i = 0; i < ci->chat_aborts; i++) {
00176             m = ci->chat_abomat[i];
00177             if (ch == ci->chat_abort[i][m]) {
00178                 if (ci->chat_abort[i][++m] == 0) {
00179 #ifdef NUTDEBUG
00180                     if (__chat_trf) {
00181                         static prog_char dbgmsg[] = "' ABORT\n";
00182                         fputs_P(dbgmsg, __chat_trs);
00183                     }
00184 #endif
00185                     return i + 4;
00186                 }
00187             } else
00188                 m = (ch == ci->chat_abort[i][0]);
00189             ci->chat_abomat[i] = m;
00190         }
00191 
00192         /*
00193          * Check for report strings.
00194          */
00195         if (ci->chat_report_state > 0) {
00196             m = ci->chat_repmat;
00197             if (ci->chat_report_state == 2) {
00198                 chat_report[m++] = ch;
00199             } else if (ch == ci->chat_report_search[m]) {
00200                 chat_report[m++] = ch;
00201                 if (ci->chat_report_search[m] == 0) {
00202                     ci->chat_report_state = 2;
00203                 }
00204             } else {
00205                 m = (ch == ci->chat_report_search[0]);
00206             }
00207             ci->chat_repmat = m;
00208         }
00209     }
00210 
00211     /*
00212      * Read the remainder of the string before NutChatSendString clears it
00213      */
00214     if (ci->chat_report_state == 2) {
00215         m = ci->chat_repmat;    /* not needed... (but not nice to remove it) */
00216         while (m < CHAT_MAX_REPORT_SIZE) {
00217             if (_read(ci->chat_fd, &ch, 1) != 1 || ch < ' ') {
00218                 break;
00219             }
00220             chat_report[m++] = ch;
00221 
00222 #ifdef NUTDEBUG
00223             if (__chat_trf) {
00224                 if (ch > 31 && ch < 127) {
00225                     fputc(ch, __chat_trs);
00226                 } else {
00227                     fprintf(__chat_trs, "\\x%02X", ch);
00228                 }
00229             }
00230 #endif
00231         }
00232         ci->chat_report_state = 0;      /* Only find first occurence */
00233         chat_report[m] = 0;
00234     }
00235 #ifdef NUTDEBUG
00236     if (__chat_trf) {
00237         static prog_char dbgmsg[] = "'\n";
00238         fputs_P(dbgmsg, __chat_trs);
00239     }
00240 #endif
00241 
00242     return 0;
00243 }
00244 
00245 /*
00246  * \return 0 on success or 2 in case of an I/O error.
00247  */
00248 static int NutChatSendString(int fd, char *str)
00249 {
00250     int rc = 0;
00251     u_char eol = 1;
00252     u_char skip;
00253     char ch;
00254 
00255 #ifdef NUTDEBUG
00256     if (__chat_trf) {
00257         static prog_char dbgfmt[] = "Send '%s'\n";
00258         fprintf_P(__chat_trs, dbgfmt, str);
00259     }
00260 #endif
00261 
00262     /* Flush input buffer. */
00263     _read(fd, 0, 0);
00264     while (*str && eol && rc == 0) {
00265         ch = *str++;
00266         skip = 0;
00267         if (ch == '^') {
00268             ch = *str++;
00269             ch &= 0x1f;
00270         } else if (ch == '\\') {
00271             ch = *str++;
00272             switch (ch) {
00273             case 'b':
00274                 ch = '\b';
00275                 break;
00276             case 'c':
00277                 eol = 0;
00278                 skip = 1;
00279                 break;
00280             case 'd':
00281                 NutSleep(1000);
00282                 skip = 1;
00283                 break;
00284             case 'n':
00285                 ch = '\n';
00286                 break;
00287             case 'N':
00288                 ch = 0;
00289                 break;
00290             case 'p':
00291                 NutDelay(100);
00292                 skip = 1;
00293                 break;
00294             case 'r':
00295                 ch = '\r';
00296                 break;
00297             case 's':
00298                 ch = ' ';
00299                 break;
00300             case 't':
00301                 ch = '\t';
00302                 break;
00303             default:
00304                 if (ch >= '0' && ch <= '7') {
00305                     ch &= 0x07;
00306                     if (*str >= '0' && *str <= '7') {
00307                         ch <<= 3;
00308                         ch |= *str++ & 0x07;
00309                         if (*str >= '0' && *str <= '7') {
00310                             ch <<= 3;
00311                             ch |= *str++ & 0x07;
00312                         }
00313                     }
00314                 }
00315                 break;
00316             }
00317         }
00318         if (skip)
00319             skip = 0;
00320         else {
00321             NutDelay(10);
00322             if (_write(fd, &ch, 1) != 1)
00323                 rc = 2;
00324             else
00325                 _write(fd, 0, 0);
00326         }
00327     }
00328     if (eol && rc == 0 && _write(fd, "\r", 1) != 1)
00329         rc = 2;
00330     else
00331         _write(fd, 0, 0);
00332 
00333     return rc;
00334 }
00335 
00336 /*
00337  * \param ci Pointer to a NUTCHAT structure, which must have been 
00338  *           created by NutChatCreate().
00339  *
00340  * \return 0 on success, 1 in case of invalid parameters, 2 in case
00341  *         of an I/O error, 3 in case of a timeout error while waiting
00342  *         for an expected string, or the index of an abort string plus
00343  *         4, if one has been received. 
00344  */
00345 int NutChatExpect(NUTCHAT * ci, char *str)
00346 {
00347     int rc = 0;
00348     char *reply;
00349     char *subexpect;
00350 
00351     /*
00352      * Process special keywords.
00353      */
00354     if (strcmp(str, "ABORT") == 0) {
00355         ci->chat_arg = CHAT_ARG_ABORT;
00356         return 0;
00357     }
00358     if (strcmp(str, "TIMEOUT") == 0) {
00359         ci->chat_arg = CHAT_ARG_TIMEOUT;
00360         return 0;
00361     }
00362     if (strcmp(str, "REPORT") == 0) {
00363         ci->chat_repmat = 0;    /* not needed ??? */
00364         ci->chat_report_state = 1;
00365         ci->chat_arg = CHAT_ARG_REPORT;
00366         return 0;
00367     }
00368 
00369     /*
00370      * Process expected string.
00371      */
00372     while (str) {
00373         if ((reply = strechr(str, '-')) != 0) {
00374             *reply++ = 0;
00375             if ((subexpect = strechr(reply, '-')) != 0)
00376                 *subexpect++ = 0;
00377         } else
00378             subexpect = 0;
00379 
00380         if ((rc = NutChatExpectString(ci, str)) != 3 || reply == 0)
00381             break;
00382         if ((rc = NutChatSendString(ci->chat_fd, reply)) != 0)
00383             break;
00384         str = subexpect;
00385     }
00386     return rc;
00387 }
00388 
00401 int NutChatSend(NUTCHAT * ci, char *str)
00402 {
00403     char *cp;
00404     char ch;
00405     long lv;
00406 
00407     /*
00408      * Add a chat abort string.
00409      */
00410     if (ci->chat_arg == CHAT_ARG_ABORT) {
00411         ci->chat_arg = CHAT_ARG_SEND;
00412 
00413         if (ci->chat_aborts >= CHAT_MAX_ABORTS)
00414             return 1;
00415         cp = malloc(strlen(str) + 1);
00416         ci->chat_abort[ci->chat_aborts++] = cp;
00417         while (*str) {
00418             ch = *str++;
00419             if (ch == '^')
00420                 *cp = *str++ & 0x1f;
00421             else if (ch == '\\') {
00422                 ch = *str++;
00423                 switch (ch) {
00424                 case 'b':
00425                     *cp++ = '\b';
00426                     break;
00427                 case 'n':
00428                     *cp++ = '\n';
00429                     break;
00430                 case 'r':
00431                     *cp++ = '\r';
00432                     break;
00433                 case 's':
00434                     *cp++ = ' ';
00435                     break;
00436                 case 't':
00437                     *cp++ = '\t';
00438                     break;
00439                 default:
00440                     if (ch >= '0' && ch <= '7') {
00441                         ch &= 0x07;
00442                         if (*str >= '0' && *str <= '7') {
00443                             ch <<= 3;
00444                             ch |= *str++ & 0x07;
00445                             if (*str >= '0' && *str <= '7') {
00446                                 ch <<= 3;
00447                                 ch |= *str++ & 0x07;
00448                             }
00449                         }
00450                     }
00451                     if (ch)
00452                         *cp++ = ch;
00453                     break;
00454                 }
00455             } else
00456                 *cp++ = ch;
00457         }
00458         *cp = 0;
00459         return 0;
00460     }
00461 
00462     /*
00463      * Set chat timeout.
00464      */
00465     if (ci->chat_arg == CHAT_ARG_TIMEOUT) {
00466         ci->chat_arg = CHAT_ARG_SEND;
00467 
00468         lv = atol(str) * 1000L;
00469         if (lv <= 0)
00470             lv = CHAT_DEFAULT_TIMEOUT * 1000L;
00471 
00472         _ioctl(ci->chat_fd, UART_SETREADTIMEOUT, &lv);
00473 
00474         return 0;
00475     }
00476 
00477     /*
00478      * Set report string
00479      */
00480     if (ci->chat_arg == CHAT_ARG_REPORT) {
00481         ci->chat_arg = CHAT_ARG_SEND;
00482         chat_report = malloc(CHAT_MAX_REPORT_SIZE + 1);
00483         cp = malloc(strlen(str) + 1);
00484         ci->chat_report_search = cp;
00485         while (*str)
00486             *cp++ = *str++;     /* Do it the easy way, not as smart and thorough as the abort string... */
00487         *cp = 0;
00488 
00489         return 0;
00490     }
00491 
00492     /*
00493      * Send the argument string.
00494      */
00495     return NutChatSendString(ci->chat_fd, str);
00496 }
00497 
00498 
00504 NUTCHAT *NutChatCreate(int fd)
00505 {
00506     NUTCHAT *ci;
00507 
00508     if ((ci = malloc(sizeof(NUTCHAT))) != 0) {
00509         memset(ci, 0, sizeof(NUTCHAT));
00510         ci->chat_fd = fd;
00511     }
00512     return ci;
00513 }
00514 
00521 void NutChatDestroy(NUTCHAT * ci)
00522 {
00523     u_char i;
00524 
00525     if (ci) {
00526         for (i = 0; i < ci->chat_aborts; i++)
00527             free(ci->chat_abort[i]);
00528         free(ci);
00529     }
00530 }
00531 
00540 static int NutChatProc(int fd, char *script)
00541 {
00542     int rc = 0;
00543     char sendflg = 0;
00544     NUTCHAT *ci;
00545     char *arg;
00546     u_long to;
00547     u_long irto;
00548     u_long iwto;
00549 
00550     /*
00551      * Initialize the chat info structure.
00552      */
00553     if ((ci = NutChatCreate(fd)) == 0)
00554         return 2;
00555 
00556     /*
00557      * Save current and set default timeouts.
00558      */
00559     _ioctl(fd, UART_GETREADTIMEOUT, &irto);
00560     _ioctl(fd, UART_GETWRITETIMEOUT, &iwto);
00561     to = 45000;
00562     _ioctl(fd, UART_SETREADTIMEOUT, &to);
00563     to = 5000;
00564     _ioctl(fd, UART_SETWRITETIMEOUT, &to);
00565 
00566     /*
00567      * This loop splits up the chat string into arguments and 
00568      * alternating calls NutChatSend and NutChatExpect.
00569      */
00570     while (*script && rc == 0) {
00571 
00572         /*
00573          * Skip leading spaces.
00574          */
00575         if (*script == ' ' || *script == '\t' || *script == '\r' || *script == '\n') {
00576             script++;
00577             continue;
00578         }
00579 
00580         /*
00581          * Collect a quoted argument.
00582          */
00583         if (*script == '"' || *script == '\'') {
00584             char quote = *script++;
00585 
00586             arg = script;
00587             while (*script != quote) {
00588                 if (*script == 0) {
00589                     rc = 1;
00590                     break;
00591                 }
00592                 if (*script++ == '\\') {
00593                     if (*script)
00594                         ++script;
00595                 }
00596             }
00597         }
00598 
00599         /*
00600          * Collect an argument upto the next space.
00601          */
00602         else {
00603             arg = script;
00604             while (*script && *script != ' ' && *script != '\t' && *script != '\r' && *script != '\n')
00605                 ++script;
00606         }
00607 
00608         if (*script)
00609             *script++ = 0;
00610 
00611         /*
00612          * Either send or expect the collected argument.
00613          */
00614         if (rc == 0) {
00615             if (sendflg)
00616                 rc = NutChatSend(ci, arg);
00617             else
00618                 rc = NutChatExpect(ci, arg);
00619             sendflg = !sendflg;
00620         }
00621     }
00622 
00623     /*
00624      * Restore initial timeout values.
00625      */
00626     _ioctl(fd, UART_SETREADTIMEOUT, &irto);
00627     _ioctl(fd, UART_SETWRITETIMEOUT, &iwto);
00628 
00629     /*
00630      * Release allocated memory.
00631      */
00632     NutChatDestroy(ci);
00633 
00634     return rc;
00635 }
00636 
00650 int NutChat(int fd, CONST char *script)
00651 {
00652     int rc = -1;
00653     char *buf;
00654 
00655     /*
00656      * Work with a local copy of the chat string.
00657      */
00658     if ((buf = malloc(strlen(script) + 1)) != 0) {
00659         strcpy(buf, script);
00660         rc = NutChatProc(fd, buf);
00661         free(buf);
00662     }
00663     return rc;
00664 }
00665 
00677 #ifdef __HARVARD_ARCH__
00678 int NutChat_P(int fd, PGM_P script)
00679 {
00680     int rc = -1;
00681     char *buf;
00682 
00683     /*
00684      * Work with a local copy of the chat string.
00685      */
00686     if ((buf = malloc(strlen_P(script) + 1)) != 0) {
00687         strcpy_P(buf, script);
00688         rc = NutChatProc(fd, buf);
00689         free(buf);
00690     }
00691     return rc;
00692 }
00693 #endif

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/