uabstractclient.cc

00001 
00024 #include "libport/windows.hh"
00025 
00026 #include "libport/cstdio"
00027 #include <cstdlib>
00028 #include <cerrno>
00029 #include <cmath>
00030 #include "libport/sys/stat.h"
00031 
00032 #include <algorithm>
00033 #include <iostream>
00034 
00035 #include "urbi/uabstractclient.hh"
00036 #include "libport/lockable.hh"
00037 
00038 #define URBI_ERROR_TAG "[error]"
00039 #define URBI_WILDCARD_TAG "[wildcard]"
00040 
00041 namespace urbi
00042 {
00043   enum UCallbackType
00044   {
00045     UCB_,
00046     UCB_C,
00047   };
00048 
00049   static UCallbackID nextId;
00050 
00051   class UCallbackWrapperCB: public UCallbackWrapper
00052   {
00053     UCallback cb;
00054   public:
00055     UCallbackWrapperCB(UCallback cb): cb(cb)
00056     {
00057     }
00058     virtual UCallbackAction operator ()(const UMessage& msg)
00059     {
00060       return cb(msg);
00061     }
00062   };
00063 
00064 
00065   class UCallbackWrapperCCB: public UCallbackWrapper
00066   {
00067     UCustomCallback cb;
00068     void * data;
00069   public:
00070     UCallbackWrapperCCB(UCustomCallback cb, void * data): cb(cb), data(data)
00071     {
00072     }
00073     virtual UCallbackAction operator ()(const UMessage& msg)
00074     {
00075       return cb(data, msg);
00076     }
00077   };
00078 
00079 
00080   class UClientStreambuf: public std::streambuf
00081   {
00082   public:
00083     UClientStreambuf(UAbstractClient * cl)
00084       : client(cl)
00085     {
00086     }
00087 
00088   protected:
00089     virtual int overflow (int c = EOF);
00090     // Override std::basic_streambuf<_CharT, _Traits>::xsputn.
00091     virtual std::streamsize xsputn (const char* s, std::streamsize n);
00092 
00093   private:
00094     UAbstractClient* client;
00095   };
00096 
00097   int UClientStreambuf::overflow (int c )
00098   {
00099     if (c != EOF)
00100     {
00101       char ch = c;
00102       xsputn(&ch, 1);
00103     }
00104     return c;
00105   }
00106 
00107   std::streamsize UClientStreambuf::xsputn (const char* s, std::streamsize n)
00108   {
00109     client->sendBufferLock.lock();
00110     if (strlen(client->sendBuffer)+1+n > static_cast<unsigned>(client->buflen))
00111     {
00112       //error
00113       client->sendBufferLock.unlock();
00114       return 0;
00115     }
00116     int clen = strlen(client->sendBuffer);
00117     memcpy(client->sendBuffer+clen, s, n);
00118     client->sendBuffer[clen+n] = 0;
00119     if (strchr(client->sendBuffer, '&')
00120         || strchr(client->sendBuffer, '|')
00121         || strchr(client->sendBuffer, ';')
00122         || strchr(client->sendBuffer, ','))
00123     {
00124       client->effectiveSend(client->sendBuffer, strlen(client->sendBuffer));
00125       client->sendBuffer[0] = 0;
00126     }
00127     client->sendBufferLock.unlock();
00128     return n;
00129   }
00130 
00131 
00132 
00136   void
00137   UAbstractClient::notifyCallbacks(const UMessage &msg)
00138   {
00139     listLock.lock();
00140     bool inc=false;
00141     for (std::list<UCallbackInfo>::iterator it = callbackList.begin();
00142          it!=callbackList.end(); inc?it:it++, inc=false)
00143     {
00144       if (STREQ(msg.tag.c_str(), it->tag)
00145           || STREQ(it->tag, URBI_ERROR_TAG) && msg.type == MESSAGE_ERROR
00146           || STREQ(it->tag, URBI_WILDCARD_TAG))
00147       {
00148         UCallbackAction ua = it->callback(msg);
00149         if (ua == URBI_REMOVE)
00150         {
00151           delete &(it->callback);
00152           it=callbackList.erase(it);
00153           inc = true;
00154         }
00155       }
00156     }
00157     listLock.unlock();
00158   }
00159 
00166   UAbstractClient::UAbstractClient(const char *_host, int _port, int _buflen)
00167     : std::ostream(new UClientStreambuf(this)),
00168       // Irk, *new...
00169       sendBufferLock(*new libport::Lockable()),
00170       listLock(*new libport::Lockable()),
00171       host (NULL),
00172       port (_port),
00173       buflen (_buflen),
00174       rc (0),
00175 
00176       recvBuffer (NULL),
00177       recvBufferPosition (0),
00178 
00179       binaryBuffer (NULL),
00180       parsePosition (0),
00181       inString (false),
00182       nBracket (0),
00183       binaryMode(false),
00184       system(false),
00185       uid (0),
00186       stream(this)
00187   {
00188     getStream().setf(std::ios::fixed);
00189     host = new char[strlen(_host) + 1];
00190     if (!host)
00191     {
00192       rc = -1;
00193       return;
00194     }
00195     strcpy(host, _host);
00196 
00197     recvBuffer = new char[buflen];
00198     if (!recvBuffer)
00199     {
00200       rc = -1;
00201       //printf("UAbstractClient::UAbstractClient out of memory\n");
00202       return;
00203     }
00204     recvBuffer[0] = 0;
00205 
00206     sendBuffer = new char[buflen];
00207     if (!sendBuffer)
00208     {
00209       rc = -1;
00210       //printf("UAbstractClient::UAbstractClient out of memory\n");
00211       return;
00212     }
00213     sendBuffer[0] = 0;
00214   }
00215 
00216   UAbstractClient::~UAbstractClient()
00217   {
00218     delete [] host;
00219     delete [] recvBuffer;
00220     delete [] sendBuffer;
00221   }
00222 
00227   int
00228   UAbstractClient::startPack()
00229   {
00230     sendBufferLock.lock();
00231     return 0;
00232   }
00233 
00234   int
00235   UAbstractClient::endPack()
00236   {
00237     int retval = effectiveSend(sendBuffer, strlen(sendBuffer));
00238     sendBuffer[0] = 0;
00239     sendBufferLock.unlock();
00240     return retval;
00241   }
00242 
00243 
00246   int
00247   UAbstractClient::send(const char *command, ...)
00248   {
00249     if (rc)
00250       return -1;
00251     va_list arg;
00252     va_start(arg, command);
00253     sendBufferLock.lock();
00254     rc = vpack(command, arg);
00255     va_end(arg);
00256     if (rc < 0)
00257     {
00258       sendBufferLock.unlock();
00259       return rc;
00260     }
00261     rc = effectiveSend(sendBuffer, strlen(sendBuffer));
00262     sendBuffer[0] = 0;
00263     sendBufferLock.unlock();
00264     return rc;
00265   }
00266 
00267   int UAbstractClient::send(UValue &v)
00268   {
00269     switch (v.type)
00270     {
00271       case DATA_DOUBLE:
00272         send("%lf", v.val);
00273         break;
00274       case DATA_STRING:
00275         send("\"%s\"", v.stringValue->c_str());
00276         break;
00277       case DATA_BINARY:
00278         if (v.binary->type != BINARY_NONE
00279             && v.binary->type != BINARY_UNKNOWN)
00280           v.binary->buildMessage();
00281         sendBin(v.binary->common.data, v.binary->common.size,
00282                 "BIN %d %s;", v.binary->common.size,
00283                 v.binary->message.c_str());
00284         break;
00285       case DATA_LIST:
00286       {
00287         send("[");
00288         int sz = v.list->size();
00289         for (int i = 0; i < sz; ++i)
00290         {
00291           send((*v.list)[i]);
00292           if (i != sz-1)
00293             send(" , ");
00294         }
00295         send("]");
00296       }
00297       break;
00298       case DATA_OBJECT:
00299       {
00300         send("OBJ %s [", v.object->refName.c_str());
00301         int sz = v.object->size();
00302         for (int i = 0; i < sz; ++i)
00303         {
00304           send("%s :", (*v.object)[i].name.c_str());
00305           send(*((*v.object)[i].val) );
00306           if (i != sz-1)
00307             send(" , ");
00308         }
00309         send("]");
00310       }
00311       break;
00312       case DATA_VOID:
00313         break;
00314     };
00315     return 0;
00316   }
00317 
00318 
00319 
00324   int
00325   UAbstractClient::pack(const char *command, ...)
00326   {
00327     if (rc)
00328       return -1;
00329     va_list arg;
00330     va_start(arg, command);
00331     rc=vpack(command, arg);
00332     va_end(arg);
00333     return rc;
00334   }
00335 
00336 
00337   int
00338   UAbstractClient::vpack(const char *command, va_list arg)
00339   {
00340     //expand
00341     if (rc)
00342       return -1;
00343 #if 0  //disabled, crashes
00344     int size = vsnprintf(NULL, 0, command, arg);
00345     va_end(arg);
00346     if (strlen(sendBuffer) + size + 1 > buflen)
00347       return -1;
00348     else
00349     {
00350 #endif
00351       sendBufferLock.lock();
00352       vsprintf(&sendBuffer[strlen(sendBuffer)], command, arg);
00353       sendBufferLock.unlock();
00354       va_end(arg);
00355       return 0;
00356 #if 0
00357     }
00358 #endif
00359   }
00360 
00361 
00362   int
00363   UAbstractClient::sendFile(const char *name)
00364   {
00365     if (rc)
00366       return -1;
00367     FILE *fd;
00368     fd = fopen(name, "r");
00369     if (fd == NULL)
00370       return -1;
00371     int size;
00372     struct stat s;
00373     stat(name, &s);
00374     size = s.st_size;
00375     sendBufferLock.lock();
00376     if (!canSend(size))
00377     {
00378       sendBufferLock.unlock();
00379       return -1;
00380     }
00381 
00382     while (!feof(fd))
00383     {
00384       int res = fread(sendBuffer, 1, buflen, fd);
00385       effectiveSend(sendBuffer, res);
00386     }
00387     fclose(fd);
00388     sendBuffer[0] = 0;
00389     sendBufferLock.unlock();
00390     return 0;
00391   }
00392 
00393 
00394   int
00395   UAbstractClient::sendBin(const void *buffer, int len)
00396   {
00397     return sendBin(buffer, len, NULL, 0);
00398   }
00399 
00400 
00401   int
00402   UAbstractClient::sendBin(const void *buffer, int len,
00403                            const char *header, ...)
00404   {
00405     if (rc)
00406       return -1;
00407     sendBufferLock.lock();
00408     if (header)
00409     {
00410       va_list arg;
00411       va_start(arg, header);
00412       vpack(header, arg);
00413       va_end(arg);
00414       if (!canSend(strlen(sendBuffer) + len))
00415       {
00416         sendBufferLock.unlock();
00417         return -1;
00418       }
00419 
00420       effectiveSend(sendBuffer, strlen(sendBuffer));
00421     }
00422 
00423     int res = effectiveSend(buffer, len);
00424     sendBuffer[0] = 0;
00425     sendBufferLock.unlock();
00426     return res;
00427   }
00428 
00429   struct sendSoundData
00430   {
00431     char * buffer;
00432     int bytespersec;
00433     int length;
00434     int pos;
00435     char * device;
00436     char * tag;
00437     char formatString[50];
00438     USoundFormat format;
00439     UAbstractClient * uc;
00440     bool startNotify;
00441   };
00442 
00443   struct wavheader
00444   {
00445     char riff[4];
00446     int length;
00447     char wave[4];
00448     char fmt[4];
00449     int lnginfo;
00450     short one;
00451     short channels;
00452     int freqechant;
00453     int bytespersec;
00454     short bytesperechant;
00455     short bitperchannel;
00456     char data[4];
00457     int datalength;
00458   };
00459 
00460   static UCallbackAction sendSound_(void * cb, const UMessage &msg)
00461   {
00462     //the idea is to cut the sound into small chunks,
00463     //add a header and send each chunk separately
00464     //create the header.
00465     // printf("sound message: %s %d\n", msg.systemValue, msg.type);
00466     static const int CHUNK_SIZE = 32 * 8*60;
00467     // static const int SUBCHUNK_SIZE = CHUNK_SIZE; //1024;
00468 
00469 
00470     sendSoundData *s=(sendSoundData *)cb;
00471     /*
00472      if (msg.type != MESSAGE_SYSTEM)
00473      return URBI_CONTINUE;
00474      if (strstr(msg.systemValue, "start") && s->startNotify==false)
00475      {
00476      s->startNotify = true;
00477      s->uc->notifyCallbacks(UMessage(*s->uc, 0, s->tag, "*** start"));
00478      }
00479      if (!strstr(msg.systemValue, "stop"))
00480      return URBI_CONTINUE;
00481      */
00482     /*wavheader wh =
00483      {
00484      {'R', 'I', 'F', 'F'},
00485      44-8,
00486      {'W', 'A', 'V', 'E'},
00487      {'f', 'm', 't', ' '},
00488      16, 1, 1, 16000, 32000, 2, 16,
00489      {'d', 'a', 't', 'a'},
00490      0}; // no comment...
00491      */
00492     //handle next chunk
00493     if (s->format == SOUND_WAV && s->pos==0)
00494       s->pos = sizeof (wavheader);
00495     int tosend = (s->length-s->pos > CHUNK_SIZE) ? CHUNK_SIZE:s->length-s->pos;
00496 
00497     //printf("%d start chunk of size %d at offset %d\n", 0, tosend, s->pos);
00498     int playlength = tosend *1000 / s->bytespersec;
00499     s->uc->send("%s.val = BIN %d %s %s;",
00500                 s->device,
00501                 (int)(tosend+ ((s->format == SOUND_WAV)?sizeof (wavheader):0)),
00502                 (s->format == SOUND_WAV)?"wav":"raw",
00503                 s->formatString
00504       );
00505     if (s->format == SOUND_WAV)
00506     {
00507       wavheader wh;
00508       memcpy(&wh, s->buffer, sizeof (wh));
00509       wh.datalength=tosend;
00510       wh.length=tosend+44-8;
00511       s->uc->sendBin(&wh, sizeof (wavheader));
00512     }
00513 
00514 
00515 #if 0
00516     // this appears to be useless
00517     int msecpause = ((SUBCHUNK_SIZE / 32) * 10) / 14;
00518     int spos = 0;
00519     while (spos != tosend)
00520     {
00521       int ts = SUBCHUNK_SIZE;
00522       if (ts > tosend-spos)
00523         ts = tosend-spos;
00524       printf("%d chunk\n", mtime());
00525       s->uc->sendBin(s->buffer, ts);
00526       s->buffer += ts;
00527       spos += ts;
00528       usleep(msecpause);
00529     }
00530 #endif
00531 
00532     s->uc->sendBin(s->buffer+s->pos, tosend);
00533     s->uc->send("wait(%s.remain < %d);"
00534                 " %s: ping;", s->device, playlength / 2, msg.tag.c_str());
00535     // printf("%d end sending chunk\n", 0);
00536     s->pos += tosend;
00537     if (s->pos >= s->length )
00538     {
00539       //printf("over: %d %d\n", URBI_REMOVE, URBI_CONTINUE);
00540       //if (s->tag && s->tag[0])
00541       //  s->uc->notifyCallbacks(UMessage(*s->uc, 0, s->tag, "*** stop"));
00542 
00543       s->uc->send("speaker->blend=speaker.sendsoundsaveblend;");
00544       if (s->tag && s->tag[0])
00545         s->uc->send("%s: 1;", s->tag);
00546       free(s->buffer);
00547       free(s->tag);
00548       free(s->device);
00549       delete s;
00550       return URBI_REMOVE;
00551     }
00552     return URBI_CONTINUE;
00553   }
00554 
00561   int
00562   UAbstractClient::sendSound(const char* device, const USound& sound,
00563                              const char* tag)
00564   {
00565     if (sound.soundFormat == SOUND_MP3)
00566     {
00567       //we don't handle chunkuing for this format
00568       return sendBin(sound.data, sound.size,
00569                      "%s +report:  %s.val = BIN %d mp3;",
00570                      tag, device, sound.size);
00571     }
00572     if (sound.soundFormat == SOUND_OGG)
00573     {
00574       //we don't handle chunkuing for this format
00575       return sendBin(sound.data, sound.size,
00576                      "%s +report:  %s.val = BIN %d ogg;",
00577                      tag, device, sound.size);
00578     }
00579 
00580     if (sound.soundFormat == SOUND_WAV
00581         || sound.soundFormat == SOUND_RAW)
00582     {
00583       send("speaker.sendsoundsaveblend = speaker->blend;"
00584            "speaker->blend=queue;");
00585       sendSoundData *s = new sendSoundData();
00586       char utag[16];
00587       makeUniqueTag(utag);
00588       s->bytespersec = sound.channels * sound.rate * (sound.sampleSize / 8);
00589       s->uc = this;
00590       s->buffer = static_cast<char*> (malloc (sound.size));
00591       memcpy(s->buffer, sound.data, sound.size);
00592       s->length = sound.size;
00593       s->tag = tag ? strdup(tag) : 0;
00594       s->device = strdup(device);
00595       s->pos = 0;
00596       s->format = sound.soundFormat;
00597       if (sound.soundFormat == SOUND_RAW)
00598         sprintf(s->formatString, "%d %d %d %d",
00599                 sound.channels, sound.rate, sound.sampleSize,
00600                 (int) sound.sampleFormat);
00601       else
00602         s->formatString[0] = 0;
00603       s->startNotify = false;
00604       UCallbackID cid=setCallback(sendSound_, s, utag);
00605       //invoke it 2 times to queue sound
00606       if (sendSound_(s, UMessage(*this, 0, utag, "*** stop",
00607                                  std::list<BinaryData>()))==URBI_CONTINUE)
00608       {
00609         if (sendSound_(s, UMessage(*this, 0, utag, "*** stop",
00610                                    std::list<BinaryData>()))==URBI_REMOVE)
00611         {
00612           deleteCallback(cid);
00613         }
00614       }
00615       else
00616         deleteCallback(cid);
00617       return 0;
00618     }
00619     //unrecognized format
00620     return 1;
00621   }
00622 
00623   UCallbackID
00624   UAbstractClient::setCallback(UCallback cb, const char *tag)
00625   {
00626     return addCallback(tag, *new UCallbackWrapperCB(cb));
00627   }
00628 
00629   UCallbackID
00630   UAbstractClient::setCallback(UCustomCallback cb,
00631                                void* cbData,
00632                                const char* tag)
00633   {
00634     return addCallback(tag, *new UCallbackWrapperCCB(cb, cbData));
00635   }
00636 
00639   int
00640   UAbstractClient::getAssociatedTag(UCallbackID id, char * tag)
00641   {
00642     listLock.lock();
00643     std::list<UCallbackInfo>:: iterator it = find(callbackList.begin(),
00644                                                   callbackList.end(), id);
00645     if (it == callbackList.end())
00646     {
00647       listLock.unlock();
00648       return 0;
00649     }
00650     strcpy(tag, it->tag);
00651     listLock.unlock();
00652     return 1;
00653   }
00654 
00655 
00658   int
00659   UAbstractClient::deleteCallback(UCallbackID callbackID)
00660   {
00661     listLock.lock();
00662     std::list<UCallbackInfo>:: iterator it = find(callbackList.begin(),
00663                                                   callbackList.end(),
00664                                                   callbackID);
00665     if (it == callbackList.end())
00666     {
00667       listLock.unlock();
00668       return 0;
00669     }
00670     delete &(it->callback);
00671     callbackList.erase(it);
00672     listLock.unlock();
00673     return 1;
00674   }
00675 
00676   UCallbackID
00677   UAbstractClient::sendCommand(UCallback cb, const char *cmd, ...)
00678   {
00679     char tag[16];
00680     makeUniqueTag(tag);
00681     char *mcmd = new char[strlen(cmd) + strlen(tag) + 5];
00682     sprintf(mcmd, "%s: %s", tag, cmd);
00683     UCallbackID cid = setCallback(cb, tag);
00684     sendBufferLock.lock();
00685     va_list arg;
00686     va_start(arg, cmd);
00687     vpack(mcmd, arg);
00688     va_end(arg);
00689     int retval = effectiveSend(sendBuffer, strlen(sendBuffer));
00690     sendBuffer[0] = 0;
00691     sendBufferLock.unlock();
00692     delete []  mcmd;
00693     if (retval)
00694     {
00695       deleteCallback(cid);
00696       return UINVALIDCALLBACKID;
00697     }
00698     return cid;
00699   }
00700 
00701   UCallbackID
00702   UAbstractClient::sendCommand(UCustomCallback cb, void *cbData,
00703                                const char *cmd, ...)
00704   {
00705     char tag[16];
00706     makeUniqueTag(tag);
00707     char *mcmd = new char[strlen(cmd) + strlen(tag) + 10];
00708     sprintf(mcmd, "%s: %s", tag, cmd);
00709     UCallbackID cid = setCallback(cb, cbData, tag);
00710     sendBufferLock.lock();
00711     va_list arg;
00712     va_start(arg, cmd);
00713     vpack(mcmd, arg);
00714     va_end(arg);
00715     int retval = effectiveSend(sendBuffer, strlen(sendBuffer));
00716     sendBuffer[0] = 0;
00717     sendBufferLock.unlock();
00718     delete []mcmd;
00719     if (retval)
00720     {
00721       deleteCallback(cid);
00722       return UINVALIDCALLBACKID;
00723     }
00724     return cid;
00725 
00726   }
00727 
00728   int
00729   UAbstractClient::putFile(const char * localName, const char * remoteName)
00730   {
00731     int len;
00732     struct stat st;
00733     if (stat(localName, &st) == -1)
00734       return 1;
00735     len = st.st_size;
00736     sendBufferLock.lock();
00737     if (!canSend(len + strlen(remoteName) + 20))
00738     {
00739       sendBufferLock.unlock();
00740       return -1;
00741     }
00742 
00743     if (!remoteName)
00744       remoteName = localName;
00745     send("save(\"%s\", \"", remoteName);
00746     int res = sendFile(localName);
00747     send("\");");
00748     sendBufferLock.unlock();
00749     return res;
00750   }
00751 
00752   int
00753   UAbstractClient::putFile(const void * buffer, int length,
00754                            const char * remoteName)
00755   {
00756     if (!canSend(length+strlen(remoteName)+ 20))
00757     {
00758       sendBufferLock.unlock();
00759       return -1;
00760     }
00761     send("save(\"%s\", \"", remoteName);
00762     sendBin(buffer, length);
00763     send("\");");
00764     sendBufferLock.unlock();
00765     return 0;
00766   }
00767 
00768   void
00769   UAbstractClient::makeUniqueTag(char* tag)
00770   {
00771     sprintf(tag, "URBI_%d", ++uid);
00772     return;
00773   }
00774 
00779   void
00780   UAbstractClient::processRecvBuffer()
00781   {
00782     char* endline;
00783     while (true)
00784     {
00785       if (binaryMode)
00786       {
00787         //Receiving binary. Append to binaryBuffer;
00788         int len = std::min(recvBufferPosition - endOfHeaderPosition,
00789                            binaryBufferLength - binaryBufferPosition);
00790         if (binaryBuffer)
00791           memcpy (static_cast<char*> (binaryBuffer) + binaryBufferPosition,
00792                   recvBuffer + endOfHeaderPosition, len);
00793         binaryBufferPosition += len;
00794 
00795         if (binaryBufferPosition == binaryBufferLength)
00796         {
00797           //Finished receiving binary.
00798           //append
00799           BinaryData bd;
00800           bd.size = binaryBufferLength;
00801           bd.data = binaryBuffer;
00802           bins.push_back(bd);
00803           binaryBuffer = 0;
00804 
00805           if (nBracket == 0)
00806           {
00807             //end of command, send
00808             //dumb listLock.lock();
00809             UMessage msg(*this, currentTimestamp, currentTag, currentCommand,
00810                          bins);
00811             notifyCallbacks(msg);
00812             //unlistLock.lock();
00813 
00814             while (!bins.empty())
00815             {
00816               free(bins.front().data);
00817               bins.pop_front();
00818             }
00819             //flush
00820             parsePosition = 0;
00821             //Move the extra we received
00822             memmove(recvBuffer,
00823                     recvBuffer + endOfHeaderPosition + len,
00824                     recvBufferPosition - len - endOfHeaderPosition);
00825             recvBufferPosition = recvBufferPosition - len - endOfHeaderPosition;
00826           }
00827           else
00828           {
00829             // not over yet
00830             //leave parseposition where it is
00831             //move the extra (parsePosition = endOfHeaderPosition)
00832             memmove(recvBuffer+parsePosition,
00833                     recvBuffer + endOfHeaderPosition + len,
00834                     recvBufferPosition - len - endOfHeaderPosition);
00835             recvBufferPosition = recvBufferPosition - len;
00836           }
00837           binaryBuffer = 0;
00838           binaryMode = false;
00839 
00840           //Reenter loop.
00841           continue;
00842         }
00843         else
00844         {
00845           //Not finished receiving binary.
00846           recvBufferPosition = endOfHeaderPosition;
00847           return;
00848         }
00849       }
00850       else
00851       {
00852         //Not in binary mode.
00853         endline = static_cast<char*> (memchr(recvBuffer+parsePosition, '\n',
00854                                              recvBufferPosition));
00855         if (!endline)
00856           return; //no new end of command/start of binary: wait
00857 
00858 #if 0
00859         //check
00860         printf("ding %15s\n", recvBuffer);
00861         endline2 = static_cast<char*> (memchr(recvBuffer, 0,
00862                                               recvBufferPosition));
00863         if ((unsigned int) endline - (unsigned int) recvBuffer > 50U)
00864           printf("WARNING, header unexpectedly long\n");
00865         if ((unsigned int) endline > (unsigned int) endline2 && endline2)
00866           printf("WARNING, 0 before newline\n");
00867 #endif
00868 
00869         //parse the line
00870 #if DEBUG
00871         printf("%d parse line: --%s--\n", mtime(), recvBuffer);
00872 #endif
00873         if (parsePosition == 0) // parse header
00874         {
00875           int found = sscanf(recvBuffer, "[%d:%64[A-Za-z0-9_.]]",
00876                              &currentTimestamp, currentTag);
00877           if (found != 2)
00878           {
00879             found = sscanf(recvBuffer, "[%d]", &currentTimestamp);
00880             if (found == 1)
00881               currentTag[0] = 0;
00882             else // failure
00883             {
00884               printf("UAbstractClient::read, fatal error parsing header");
00885               printf(" line was '%s'\n", recvBuffer);
00886               currentTimestamp = 0;
00887               strcpy(currentTag, "UNKNWN");
00888               //listLock.lock();
00889               UMessage msg(*this, 0, URBI_ERROR_TAG,
00890                            "!!! UAbstractClient::read, fatal error parsing header",
00891                            std::list<BinaryData>());
00892               notifyCallbacks(msg);
00893               //unlistLock.lock();
00894             }
00895           }
00896 
00897           currentCommand = strstr(recvBuffer, "]");
00898           if (!currentCommand) {
00899             //reset all
00900             nBracket = 0;
00901             inString = false;
00902             parsePosition = 0;
00903             recvBufferPosition = 0;
00904             return;
00905           }
00906           
00907           ++currentCommand;
00908           while (*currentCommand == ' ')
00909             ++currentCommand;
00910           system = (*currentCommand == '!' || *currentCommand == '*');
00911           parsePosition = (long) currentCommand - (long) recvBuffer;
00912          
00913           //reinit just to be sure:
00914           nBracket = 0;
00915           inString = false;
00916         }
00917 
00918         while (parsePosition < recvBufferPosition)
00919         {
00920           if (inString)
00921           {
00922             if (recvBuffer[parsePosition]=='\\')
00923             {
00924               if (parsePosition == recvBufferPosition-1)
00925               {
00926                 //we cant handle the '\\'
00927                 return;
00928               }
00929               parsePosition+=2; //ignore next character
00930               continue;
00931             }
00932             if (recvBuffer[parsePosition]=='"')
00933             {
00934               inString = false;
00935               ++parsePosition;
00936               continue;
00937             }
00938           }
00939           else
00940           {
00941             if (recvBuffer[parsePosition]=='"')
00942             {
00943               inString = true;
00944               ++parsePosition;
00945               continue;
00946             }
00947             if (recvBuffer[parsePosition]=='[')
00948             {
00949               ++nBracket;
00950               ++parsePosition;
00951               continue;
00952             }
00953             if (recvBuffer[parsePosition]==']')
00954             {
00955               --nBracket;
00956               ++parsePosition;
00957               continue;
00958             }
00959             if (recvBuffer[parsePosition]=='\n')
00960             {
00961               if (true /*XXX: handle '[' in echoed messages or errors nBracket == 0*/)
00962               {
00963                 //end of command
00964                 recvBuffer[parsePosition]=0;
00965                 //listLock.lock();
00966                 UMessage msg(*this, currentTimestamp, currentTag,
00967                              currentCommand,
00968                              bins);
00969                 notifyCallbacks(msg);
00970                 //unlistLock.lock();
00971                 //prepare for next read, copy the extra
00972                 memmove(recvBuffer, recvBuffer + parsePosition + 1,
00973                         recvBufferPosition - parsePosition - 1);
00974                 // copy beginning of next cmd
00975                 recvBufferPosition = recvBufferPosition - parsePosition - 1;
00976                 recvBuffer[recvBufferPosition] = 0;
00977                 parsePosition = 0;
00978                 while (!bins.empty())
00979                 {
00980                   free(bins.front().data);
00981                   bins.pop_front();
00982                 }
00983                 break; //restart
00984               }
00985               //this should not happen: \n should have been handled by binary code below
00986               fprintf(stderr, "FATAL PARSE ERROR\n");
00987             }
00988             if (!system && !strncmp(recvBuffer+parsePosition-3, "BIN ", 4))
00989             {
00990               //very important: scan starts below current point
00991               //compute length
00992               char * endLength;
00993               binaryBufferLength = strtol(recvBuffer+parsePosition+1, &endLength, 0);
00994               if (endLength == recvBuffer+parsePosition+1)
00995               {
00996                 printf("UClient::read, error parsing bin data length.\n");
00997                 recvBufferPosition = 0;
00998                 return;
00999               }
01000               //go to end of header
01001               while (recvBuffer[parsePosition] !='\n')
01002                 ++parsePosition; //we now we will find a \n
01003               ++parsePosition;
01004               endOfHeaderPosition = parsePosition;
01005               binaryMode = true;
01006               binaryBuffer = malloc(binaryBufferLength);
01007               binaryBufferPosition = 0;
01008               break; //restart in binarymode to handle binary
01009             }
01010           }
01011           ++parsePosition;
01012         }
01013         //either we ate all characters, or we were asked to restart
01014         if (parsePosition == recvBufferPosition)
01015           return;
01016         continue;
01017       }
01018     }
01019   }
01020 
01021   UCallbackID UAbstractClient::setWildcardCallback(UCallbackWrapper& callback)
01022   {
01023     return addCallback(URBI_WILDCARD_TAG, callback);
01024   }
01025 
01026   UCallbackID UAbstractClient::setErrorCallback(UCallbackWrapper& callback)
01027   {
01028     return addCallback(URBI_ERROR_TAG, callback);
01029   }
01030 
01031   UCallbackID UAbstractClient::setCallback(UCallbackWrapper& callback,
01032                                            const char* tag)
01033   {
01034     return addCallback(tag, callback);
01035   }
01036 
01037   UCallbackID UAbstractClient::addCallback(const char* tag,
01038                                            UCallbackWrapper& w)
01039   {
01040     listLock.lock();
01041     UCallbackInfo ci(w);
01042     strncpy(ci.tag, tag, URBI_MAX_TAG_LENGTH-1);
01043     ci.tag[URBI_MAX_TAG_LENGTH-1]=0;
01044     ci.id = ++nextId;
01045     callbackList.push_front(ci);
01046     listLock.unlock();
01047     return ci.id;
01048   }
01049 
01050 
01051   UMessage::UMessage(UAbstractClient& client, int timestamp,
01052                      const char *tag, const char *message,
01053                      std::list<BinaryData> bins)
01054     : client(client), timestamp(timestamp),  tag(tag), value(0)
01055   {
01056     rawMessage = std::string(message);
01057     while (message[0] ==' ')
01058       ++message;
01059     //parse non-value messages
01060     if (message[0] == '*')
01061     {
01062       //system message
01063       type = MESSAGE_SYSTEM;
01064       this->message = (std::string)(message+3);
01065       return;
01066     }
01067 
01068     if (message[0] == '!')
01069     {
01070       //error message
01071       type = MESSAGE_ERROR;
01072       this->message = (std::string)(message+3);
01073       return;
01074     }
01075 
01076     //value
01077     type = MESSAGE_DATA;
01078     value = new UValue();
01079     std::list<BinaryData>::iterator iter = bins.begin();
01080     int p = value->parse(message, 0, bins, iter);
01081     while (message[p] == ' ')
01082       ++p;
01083     /* no assertion can be made on message[p] because there is no terminator
01084      * for binaries */
01085     if (p < 0 || /*message[p] ||*/ iter != bins.end())
01086     {
01087       std::cerr << "PARSE ERROR in " << message << "at " << abs(p) << std::endl;
01088     }
01089   }
01090 
01091   UMessage::UMessage(const UMessage& b)
01092     : client(b.client)
01093   {
01094     rawMessage = b.rawMessage;
01095     timestamp = b.timestamp;
01096     tag = b.tag;
01097     type = b.type;
01098     value = 0;
01099     switch (type)
01100     {
01101       case MESSAGE_SYSTEM:
01102       case MESSAGE_ERROR:
01103         message = b.message;
01104         break;
01105       default:
01106         value = new UValue(*b.value);
01107         break;
01108     }
01109   }
01110 
01111 
01112   UMessage::~UMessage()
01113   {
01114     if (type != MESSAGE_SYSTEM && type != MESSAGE_ERROR && value)
01115       delete value;
01116   }
01117 
01118   std::ostream& operator <<(std::ostream &s, const UMessage &m)
01119   {
01120     s<<"["<<m.timestamp<<":"<<m.tag<<"] ";
01121     switch (m.type)
01122     {
01123       case MESSAGE_SYSTEM:
01124       case MESSAGE_ERROR:
01125         s<<m.message;
01126         break;
01127       default:
01128         s<<*m.value;
01129         break;
01130     }
01131     return s;
01132   }
01133 
01134   UClient * defaultClient=0;
01135 
01136   UClient * getDefaultClient()
01137   {
01138     return defaultClient;
01139   }
01140 
01141   void setDefaultClient(UClient * cl)
01142   {
01143     defaultClient = cl;
01144   }
01145 
01146   std::ostream& unarmorAndSend(const char * a)
01147   {
01148     std::ostream& s =
01149       (getDefaultClient()==0
01150        ? std::cerr
01151        : ((UAbstractClient*)getDefaultClient())->getStream());
01152     if (strlen(a)>2)
01153       if (a[0]=='(' && a[strlen(a)-1]==')')
01154         s.rdbuf()->sputn(a+1, strlen(a)-2);
01155       else
01156         s << a; //this is baaad, user forgot the parenthesis but was lucky
01157     return s;
01158   }
01159 
01160 } // namespace urbi

Generated on Tue Apr 10 17:45:44 2007 for URBISDK by  doxygen 1.5.1