diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | c90c389a8a8d9d8661e9772ec4144c5cf2039f23 (patch) | |
tree | 6d8391395bce9eaea4ad78958617edb20c6a7573 /lskat/lskatproc | |
download | tdegames-c90c389a8a8d9d8661e9772ec4144c5cf2039f23.tar.gz tdegames-c90c389a8a8d9d8661e9772ec4144c5cf2039f23.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegames@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'lskat/lskatproc')
-rw-r--r-- | lskat/lskatproc/KChildConnect.cpp | 124 | ||||
-rw-r--r-- | lskat/lskatproc/KChildConnect.h | 45 | ||||
-rw-r--r-- | lskat/lskatproc/KConnectTypes.h | 38 | ||||
-rw-r--r-- | lskat/lskatproc/KEMessage.cpp | 326 | ||||
-rw-r--r-- | lskat/lskatproc/KEMessage.h | 66 | ||||
-rw-r--r-- | lskat/lskatproc/KInputChildProcess.cpp | 106 | ||||
-rw-r--r-- | lskat/lskatproc/KInputChildProcess.h | 57 | ||||
-rw-r--r-- | lskat/lskatproc/KMessageEntry.cpp | 98 | ||||
-rw-r--r-- | lskat/lskatproc/KMessageEntry.h | 44 | ||||
-rw-r--r-- | lskat/lskatproc/Makefile.am | 13 | ||||
-rw-r--r-- | lskat/lskatproc/docs/Makefile.am | 4 | ||||
-rw-r--r-- | lskat/lskatproc/docs/en/Makefile.am | 4 | ||||
-rw-r--r-- | lskat/lskatproc/lskatproc.cpp | 596 | ||||
-rw-r--r-- | lskat/lskatproc/lskatproc.h | 104 | ||||
-rw-r--r-- | lskat/lskatproc/main.cpp | 28 | ||||
-rw-r--r-- | lskat/lskatproc/templates/cpp_template | 16 | ||||
-rw-r--r-- | lskat/lskatproc/templates/header_template | 16 |
17 files changed, 1685 insertions, 0 deletions
diff --git a/lskat/lskatproc/KChildConnect.cpp b/lskat/lskatproc/KChildConnect.cpp new file mode 100644 index 00000000..0308a3f0 --- /dev/null +++ b/lskat/lskatproc/KChildConnect.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** + KChildConnect.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include <stdio.h> +#include "KChildConnect.h" + +#include "KChildConnect.moc" + +KChildConnect::KChildConnect() + : QObject(0,0) +{ + input_pending=false; + inputbuffer=""; +} + +KChildConnect::~KChildConnect() +{ +} + +KR_STATUS KChildConnect::QueryStatus() +{ + return KR_OK; +} + +// Communication with process +bool KChildConnect::SendMsg(KEMessage *msg) +{ + QString sendstring=msg->ToString(); + // Debug only + if (msg->HasKey(QCString("KLogSendMsg"))) + { + char *p; + int size; + FILE *fp; + msg->GetData(QCString("KLogSendMsg"),p,size); + if (p && (fp=fopen(p,"a")) ) + { + fprintf(fp,"------------------------------------\n"); + fprintf(fp,"%s", sendstring.utf8().data()); + fclose(fp); + } + } + // end debug only + return Send(sendstring); +} + +// Send string to parent +bool KChildConnect::Send(QString str) +{ + if (!str || str.length()<1) return true; // no need to send crap + printf("%s",str.latin1()); + fflush(stdout); + return true; +} + +void KChildConnect::Receive(QString input) +{ + // Cut out CR + int len,pos; + QString tmp; + + + // Call us recursive until there are no CR left + len=KEMESSAGE_CR.length(); + pos=input.find(KEMESSAGE_CR); + if (pos>0) + { + tmp=input.left(pos); + if (tmp.length()>0) Receive(tmp); // CR free + input=input.right(input.length()-pos-len); + if (input.length()>0) Receive(input); + return ; + } + +// printf(" ---> KChildConnect::Receive: '%s'\n",(const char *)input); + if (input==QString(KEMESSAGE_HEAD) && !input_pending) + { + input_pending=true; + inputcache.clear(); + return ; + } + if (!input_pending) return ; // ignore + if (input!=QString(KEMESSAGE_TAIL)) + { + inputcache.append(input.latin1()); + return; + } + input_pending=0; + + KEMessage *msg=new KEMessage; + char *it; + for (it=inputcache.first();it!=0;it=inputcache.next()) + { + msg->AddString(QCString(it)); + } + +// printf("+- CHILDprocess:: GOT MESSAGE::Emmiting slotReceiveMsg\n"); + emit signalReceiveMsg(msg,ID); + + delete msg; +} + +void KChildConnect::SetID(int id) +{ + ID=id; +} +int KChildConnect::QueryID() +{ + return ID; +} + diff --git a/lskat/lskatproc/KChildConnect.h b/lskat/lskatproc/KChildConnect.h new file mode 100644 index 00000000..9cad1a5d --- /dev/null +++ b/lskat/lskatproc/KChildConnect.h @@ -0,0 +1,45 @@ +/*************************************************************************** + KChildConnect.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +#ifndef _KCHILDCONNECT_H_ +#define _KCHILDCONNECT_H_ + +#include <qobject.h> +#include <qstrlist.h> +#include "KEMessage.h" + + +class KChildConnect: public QObject +{ + Q_OBJECT + + protected: + QStrList inputcache; + bool input_pending; + QString inputbuffer; + int ID; + + public: + KChildConnect(); + ~KChildConnect(); + void Receive(QString input); + int QueryID(); + void SetID(int id); + + virtual bool SendMsg(KEMessage *msg); + virtual bool Send(QString str); + virtual KR_STATUS QueryStatus(); + + public slots: + + + signals: + void signalReceiveMsg(KEMessage *msg,int id); +}; + +#endif diff --git a/lskat/lskatproc/KConnectTypes.h b/lskat/lskatproc/KConnectTypes.h new file mode 100644 index 00000000..12e9561a --- /dev/null +++ b/lskat/lskatproc/KConnectTypes.h @@ -0,0 +1,38 @@ +/*************************************************************************** + KConnectTypes.h - description + ------------------- + begin : Sun Apr 9 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KCONNECTTYPES_H_ +#define _KCONNECTTYPES_H_ + +enum KGM_TYPE {KGM_TYPE_INVALID=0,KGM_TYPE_SHORT=1,KGM_TYPE_LONG=2, + KGM_TYPE_FLOAT=3,KGM_TYPE_DATA=4}; + +enum KG_INPUTTYPE { + KG_INPUTTYPE_INVALID=0, + KG_INPUTTYPE_INTERACTIVE=1, + KG_INPUTTYPE_PROCESS=2, + KG_INPUTTYPE_REMOTE=3}; + +enum KR_STATUS { + KR_NO_SOCKET=-2, + KR_WAIT_FOR_CLIENT=-1, + KR_INVALID=0, + // >0 OK + KR_OK=1, + KR_CLIENT=2, + KR_SERVER=3 + }; +#endif diff --git a/lskat/lskatproc/KEMessage.cpp b/lskat/lskatproc/KEMessage.cpp new file mode 100644 index 00000000..db598573 --- /dev/null +++ b/lskat/lskatproc/KEMessage.cpp @@ -0,0 +1,326 @@ +/*************************************************************************** + KEMessage.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include "KEMessage.h" + +void KEMessage::AddEntry(QString key,KMessageEntry *entry) +{ + // printf(" AddingEntry: %s with data field %p\n",(char *)key,entry->QueryData()); + if (!entry) return ; + dict.insert(key,entry); + keys.append(key.latin1()); +} + +void KEMessage::AddDataType(QString key,int size,const char *data,KGM_TYPE type) +{ +// printf("AddDataType for %s size=%d\n",(const char *)key,size); + if (size<=0) return ; + KMessageEntry *entry=new KMessageEntry; + entry->SetType(type); + entry->CopyData(size,data); + AddEntry(key,entry); +} + +void KEMessage::AddData(QString key,short data) +{ + AddDataType(key,sizeof(short),(char *)&data,KGM_TYPE_SHORT); +} + +void KEMessage::AddData(QString key,long data) +{ + AddDataType(key,sizeof(long),(char *)&data,KGM_TYPE_LONG); +} + +void KEMessage::AddData(QString key,float data) +{ + AddDataType(key,sizeof(float),(char *)&data,KGM_TYPE_FLOAT); +} + +void KEMessage::AddData(QString key,const char *data,int size) +{ + if (size<0) size=strlen(data)+1; // +1 for 0 Byte + AddDataType(key,size,data,KGM_TYPE_DATA); +} + +KGM_TYPE KEMessage::QueryType(QString key) +{ + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return (KGM_TYPE)0; + return entry->QueryType(); +} + +bool KEMessage::HasKey(QString key) +{ + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + return true; +} + +bool KEMessage::GetData(QString key,short &s) +{ + short *result; + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + if (entry->QueryType()!=KGM_TYPE_SHORT) return false; + // printf("GetShortData: %p for %s\n",entry->QueryData(),(char *)key); + result=(short *)entry->QueryData(); + s=*result; + return true; +} + +bool KEMessage::GetData(QString key,long &l) +{ + long *result; + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + if (entry->QueryType()!=KGM_TYPE_LONG) return false; + result=(long *)entry->QueryData(); + l=*result; + return true; +} + +bool KEMessage::GetData(QString key,float &f) +{ + float *result; + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + if (entry->QueryType()!=KGM_TYPE_FLOAT) return false; + // printf("GetFloatData: %p for %s\n",entry->QueryData(),(char *)key); + result=(float *)entry->QueryData(); + f=*result; + return true; +} + +bool KEMessage::GetData(QString key,char * &c,int &size) +{ + KMessageEntry *entry; + entry=dict.find(key); + if (!entry) return false; + if (entry->QueryType()!=KGM_TYPE_DATA) return false; + c=entry->QueryData(); + size=entry->QuerySize(); + return true; +} + +QString KEMessage::EntryToString(char *key,KMessageEntry *entry) +{ + QString s,tmp; + int size,i; + KGM_TYPE type; + char *data; + s=QCString(""); + if (!entry) return s; + size=entry->QuerySize(); + type=entry->QueryType(); + data=entry->QueryData(); + + // Key + /* + tmp.sprintf("%s%s%d%s%d%s", + key,KEMESSAGE_SEP, + size,KEMESSAGE_SEP, + (int)type,KEMESSAGE_SEP); + */ + tmp=QCString(key); + s+=tmp; + s+=KEMESSAGE_SEP; + tmp.sprintf("%d",size); + s+=tmp; + s+=KEMESSAGE_SEP; + tmp.sprintf("%d",(int)type); + s+=tmp; + s+=KEMESSAGE_SEP; + + + // We ignore the type of data and process them all as + // byte sequence + for (i=0;i<size;i++) + { + // Convert to 4 bit value ... someone can improves + tmp.sprintf("%c%c", + 'a'+(data[i]&15), + 'a'+((data[i]>>4)&15)); + s+=tmp; + } + s+=KEMESSAGE_CR; + + return s; +} + +QString KEMessage::StringToEntry(QString str,KMessageEntry *entry) +{ + int pos,oldpos,cnt,len; + QString key,size,type,data; + const char *p; + char *q; + char c; + + len=KEMESSAGE_SEP.length(); + + if (!entry) return QString(); + pos=str.find(KEMESSAGE_SEP,0); + if (pos<0) return QString(); // wrong format + key=str.left(pos); + + + oldpos=pos; + pos=str.find(KEMESSAGE_SEP,oldpos+len); + if (pos<0) return QString(); // wrong format + size=str.mid(oldpos+len,pos-oldpos-len); + + + oldpos=pos; + pos=str.find(KEMESSAGE_SEP,oldpos+len); + if (pos<0) return QString(); // wrong format + type=str.mid(oldpos+len,pos-oldpos-len); + + + data=str.right(str.length()-pos-len); + + + cnt=size.toInt(); + entry->SetType((KGM_TYPE)type.toInt()); + + // I hope this works with unicode strings as well + p=data.latin1(); + q=(char *)calloc(data.length()/2,sizeof(char)); + if (!q) return QString(); + for(pos=0;pos<cnt;pos++) + { + if (pos*2+1>(int)data.length()) return QString(); // SEVERE ERROR + c=*(p+2*pos)-'a' | ((*(p+2*pos+1)-'a')<<4); + q[pos]=c; + } + entry->CopyData(cnt,q); + + free(q); + return key; +} + +QString KEMessage::ToString() +{ + QString s; + KMessageEntry *entry; + char *it; + s=KEMESSAGE_HEAD+KEMESSAGE_CR; + for (it=keys.first();it!=0;it=keys.next()) + { + entry=dict.find(QCString(it)); + s+=EntryToString(it,entry); + } + s+=KEMESSAGE_TAIL+KEMESSAGE_CR; + return s; +} + +bool KEMessage::AddString(QString s) +{ + // break s into key,size and data + QString key; + KMessageEntry *entry=new KMessageEntry; + key=StringToEntry(s,entry); + if (!key) return false; + AddEntry(key,entry); + return true; +} +bool KEMessage::AddStringMsg(QString str) +{ + bool result; + QString data; + int pos,oldpos,len; + + len=KEMESSAGE_CR.length(); + + pos=str.find(KEMESSAGE_CR); + if (pos<0) return false; // wrong format + if (str.left(pos)!=(KEMESSAGE_HEAD)) return false; // wrong message + + do + { + oldpos=pos; + pos=str.find(KEMESSAGE_CR,oldpos+len); + if (pos<0) return false; // wrong format + data=str.mid(oldpos+len,pos-oldpos-len); + if (data!=(KEMESSAGE_TAIL)) + { + result=AddString(data); + if (!result) return false; // wrong format + } + }while(data!=(KEMESSAGE_TAIL)); + + return result; +} + +void KEMessage::RemoveAll() +{ + keys.clear(); + dict.clear(); +} + +void KEMessage::Remove(QString key) +{ + keys.remove(key.latin1()); + dict.remove(key); +} + +uint KEMessage::QueryNumberOfKeys() +{ + return keys.count(); +} +QStrList *KEMessage::QueryKeys() +{ + return &keys; +} + +KEMessage::~KEMessage() +{ + // printf("Deleteing KEMessage %p\n",this); +} +KEMessage::KEMessage() +{ + // printf("KEMessage construct %p\n",this); + dict.setAutoDelete(true); +} +KEMessage::KEMessage(KEMessage &msg) +{ + // printf("KEMessage copy constructor from %p to %p\n",&msg,this); + *this=msg; +} +KEMessage &KEMessage::operator=(KEMessage &msg) +{ + // KEMessage *newmsg=new KEMessage; + KMessageEntry *entry; + KMessageEntry *newentry; + char *it; + // printf("Assigning = KEMessage from %p to %p\n",&msg,this); + for (it=msg.keys.first();it!=0;it=msg.keys.next()) + { + entry=msg.dict.find(QCString(it)); + newentry=new KMessageEntry; + *newentry=*entry; + AddEntry(QCString(it),newentry); + + } + // return *newmsg; + return *this; +} diff --git a/lskat/lskatproc/KEMessage.h b/lskat/lskatproc/KEMessage.h new file mode 100644 index 00000000..0a1913cc --- /dev/null +++ b/lskat/lskatproc/KEMessage.h @@ -0,0 +1,66 @@ +/*************************************************************************** + KEMessage.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KEMESSAGE_H_ +#define _KEMESSAGE_H_ + +#include <string.h> +#include <qstring.h> +#include <qstrlist.h> +#include <qdict.h> +#include "KMessageEntry.h" + +#define KEMESSAGE_HEAD QString(QCString("BEGIN_V1000")) +#define KEMESSAGE_TAIL QString(QCString("END_V1000")) +#define KEMESSAGE_CR QString(QCString("\n")) +#define KEMESSAGE_SEP QString(QCString(":::")) + +class KEMessage +{ + private: + QStrList keys; + QDict<KMessageEntry> dict; + + protected: + void AddEntry(QString key,KMessageEntry *entry); + public: + QStrList *QueryKeys(); + uint QueryNumberOfKeys(); + void AddDataType(QString key,int size,const char *data,KGM_TYPE type); + void AddData(QString key,short data); + void AddData(QString key,long data); + void AddData(QString key,float data); + void AddData(QString key,const char *data,int size=-1); + bool GetData(QString key,short &s); + bool GetData(QString key,long &l); + bool GetData(QString key,float &f); + bool GetData(QString key,char * &c,int &size); + bool HasKey(QString key); + void Remove(QString key); + KGM_TYPE QueryType(QString key); + QString ToString(); + QString EntryToString(char *key,KMessageEntry *entry); + QString StringToEntry(QString str,KMessageEntry *entry); + bool AddString(QString s); + bool AddStringMsg(QString str); + void RemoveAll(); + ~KEMessage(); + KEMessage(); + KEMessage(KEMessage &msg); + KEMessage &operator=(KEMessage &msg); +}; + +#endif diff --git a/lskat/lskatproc/KInputChildProcess.cpp b/lskat/lskatproc/KInputChildProcess.cpp new file mode 100644 index 00000000..a821aa7a --- /dev/null +++ b/lskat/lskatproc/KInputChildProcess.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** + KInputChildProcess.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <qstring.h> + +#include "KInputChildProcess.h" +#include "KInputChildProcess.moc" + + +KInputChildProcess::~KInputChildProcess() +{ + delete buffer; + delete childConnect; +} +KInputChildProcess::KInputChildProcess(int size_buffer) + : QObject(0,0) +{ + buffersize=size_buffer; + if (buffersize<1) buffersize=1024; + buffer=new char[buffersize]; + inputbuffer=""; + terminateChild=false; +} +bool KInputChildProcess::exec() +{ + int pos; + QString s; + + childConnect=new KChildConnect; + if (!childConnect) return false; + connect(childConnect,SIGNAL(signalReceiveMsg(KEMessage *,int)), + this,SLOT(slotReceiveMsg(KEMessage *,int))); + do + { + // Wait for input + if (feof(stdin)) + { + sleep(1); + continue; + } + + if (!fgets(buffer,buffersize,stdin) ) + { + continue; + } + s=buffer; + s=inputbuffer+s; + // printf("ChildABC '%s'\n",(const char *)s); + // fflush(stdout); + pos=s.findRev(KEMESSAGE_CR); + if (pos<0) + { + inputbuffer=s; + } + else if (pos+KEMESSAGE_CR.length()==s.length()) + { + // CR at the end...calling receive + childConnect->Receive(s); + } + else + { + inputbuffer=s.right(s.length()-pos-KEMESSAGE_CR.length()); + s=s.left(pos+KEMESSAGE_CR.length()); + // printf("s='%s' in='%s'\n",(const char *)s,(const char *)inputbuffer); + childConnect->Receive(s); + } + }while(!terminateChild); + return true; +} + +void KInputChildProcess::Terminate() +{ + terminateChild=true; +} +bool KInputChildProcess::IsTerminated() +{ + return terminateChild; +} + +bool KInputChildProcess::ReceiveMsg(KEMessage *msg,int id) +{ + return false; +} +void KInputChildProcess::slotReceiveMsg(KEMessage *msg,int id) +{ + if (!ReceiveMsg(msg,id)) // made for virtual override + { + // otherwise emit signal + emit signalReceiveMsg(msg,id); + } +} +bool KInputChildProcess::SendMsg(KEMessage *msg) +{ + if (childConnect) return childConnect->SendMsg(msg); + return false; +} + + diff --git a/lskat/lskatproc/KInputChildProcess.h b/lskat/lskatproc/KInputChildProcess.h new file mode 100644 index 00000000..b4694df5 --- /dev/null +++ b/lskat/lskatproc/KInputChildProcess.h @@ -0,0 +1,57 @@ +/*************************************************************************** + KInputChildProcess.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KINPUTCHILDPROCESS_H_ +#define _KINPUTCHILDPROCESS_H_ + +#include <qobject.h> +#include "KEMessage.h" +#include "KChildConnect.h" + + +class KInputChildProcess : public QObject +{ + Q_OBJECT + + private: + char *buffer; + QString inputbuffer; + int buffersize; + bool terminateChild; + protected: + KChildConnect *childConnect; + + public: + KInputChildProcess(int size_buffer=4096); + ~KInputChildProcess(); + bool exec(); + virtual bool ReceiveMsg(KEMessage *msg,int id); + // Forward calls to childconnect + bool SendMsg(KEMessage *msg); + // Immediately kills child's exec ! + void Terminate(); + bool IsTerminated(); + + + public slots: + void slotReceiveMsg(KEMessage *msg,int id); + + signals: + void signalReceiveMsg(KEMessage *msg,int id); +}; + + +#endif diff --git a/lskat/lskatproc/KMessageEntry.cpp b/lskat/lskatproc/KMessageEntry.cpp new file mode 100644 index 00000000..e91f645a --- /dev/null +++ b/lskat/lskatproc/KMessageEntry.cpp @@ -0,0 +1,98 @@ +/*************************************************************************** + KMessageEntry.cpp - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +/*************************************************************************** + FILENAME| - description + ------------------- + begin : Tue Apr 4 2000 + copyright : (C) |1995-2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include "KMessageEntry.h" + + +void KMessageEntry::SetType(KGM_TYPE t) +{ + type=t; +} + +KGM_TYPE KMessageEntry::QueryType() +{ + return type; +} + +int KMessageEntry::QuerySize() +{ + return size; +} + +char * KMessageEntry::QueryData() +{ + return data; +} + +bool KMessageEntry::CopyData(int s,const char *c) +{ + if (s<1) return false; + data=(char *)calloc(s,1); + if (!data) return false; + // printf(" MessageEntry Copy Data to calloc %p\n",data); + memcpy(data,c,s); + size=s; + return true; +} + +KMessageEntry::KMessageEntry() +{ + // printf("KMessageEntry construct %p\n",this); + size=0; + type=(KGM_TYPE)0; + data=(char *)0; +} + +KMessageEntry::KMessageEntry(KMessageEntry &entry) +{ + // printf("KMessageEntry copy constructor from %p to %p\n",&entry,this); + *this=entry; +} +KMessageEntry &KMessageEntry::operator=(KMessageEntry &entry) +{ + // printf("KMessageEntry operator= from %p to %p\n",&entry,this); + SetType(entry.type); + CopyData(entry.size,entry.data); + return *this; +} + +KMessageEntry::~KMessageEntry() +{ + // printf("MessageEntry destructor %p\n",this); + // printf(" MessageEntry free %p\n",data); + if (data) free(data); + data=(char *)0; +} + diff --git a/lskat/lskatproc/KMessageEntry.h b/lskat/lskatproc/KMessageEntry.h new file mode 100644 index 00000000..947ce1ff --- /dev/null +++ b/lskat/lskatproc/KMessageEntry.h @@ -0,0 +1,44 @@ +/*************************************************************************** + KMessageEntry.h - description + ------------------- + begin : Tue May 2 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _KMESSAGEENTRY_H_ +#define _KMESSAGEENTRY_H_ + +#include <string.h> +#include "KConnectTypes.h" + + + +class KMessageEntry +{ + private: + int size; + KGM_TYPE type; + char *data; + + public: + void SetType(KGM_TYPE t); + KGM_TYPE QueryType(); + int QuerySize(); + char *QueryData(); + bool CopyData(int s,const char *c); + KMessageEntry(); + KMessageEntry(KMessageEntry &entry); + KMessageEntry &operator=(KMessageEntry &entry); + ~KMessageEntry(); +}; + +#endif diff --git a/lskat/lskatproc/Makefile.am b/lskat/lskatproc/Makefile.am new file mode 100644 index 00000000..4f99375b --- /dev/null +++ b/lskat/lskatproc/Makefile.am @@ -0,0 +1,13 @@ +bin_PROGRAMS = lskatproc + +lskatproc_SOURCES = lskatproc.cpp KChildConnect.cpp KInputChildProcess.cpp KEMessage.cpp KMessageEntry.cpp main.cpp + +lskatproc_LDADD = $(LIB_KFILE) + +# set the include path for X, qt and KDE +INCLUDES= $(all_includes) + +METASOURCES = AUTO + +# the library search path. +lskatproc_LDFLAGS = $(all_libraries) $(KDE_RPATH) diff --git a/lskat/lskatproc/docs/Makefile.am b/lskat/lskatproc/docs/Makefile.am new file mode 100644 index 00000000..271bd418 --- /dev/null +++ b/lskat/lskatproc/docs/Makefile.am @@ -0,0 +1,4 @@ +####### kdevelop will overwrite this part!!! (begin)########## + + +####### kdevelop will overwrite this part!!! (end)############ diff --git a/lskat/lskatproc/docs/en/Makefile.am b/lskat/lskatproc/docs/en/Makefile.am new file mode 100644 index 00000000..271bd418 --- /dev/null +++ b/lskat/lskatproc/docs/en/Makefile.am @@ -0,0 +1,4 @@ +####### kdevelop will overwrite this part!!! (begin)########## + + +####### kdevelop will overwrite this part!!! (end)############ diff --git a/lskat/lskatproc/lskatproc.cpp b/lskat/lskatproc/lskatproc.cpp new file mode 100644 index 00000000..c1fdcfba --- /dev/null +++ b/lskat/lskatproc/lskatproc.cpp @@ -0,0 +1,596 @@ +/*************************************************************************** + lskatproc.cpp - description + ------------------- + begin : Sun Apr 9 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include <unistd.h> +#include <stdlib.h> +#include <time.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "lskatproc.h" + +#define MIN_TIME 1000 // usec + +// ------------ class game --------------------------------- +lgame::lgame() +{ + int i; + for (i=0;i<14;i++) cardvalues[i]=0; + cardvalues[(int)Ace]=11; + cardvalues[(int)Ten]=10; + cardvalues[(int)King]=4; + cardvalues[(int)Queen]=3; + cardvalues[(int)Jack]=2; + curmove[0]=-1; + curmove[1]=-1; + score[0]=0; + score[1]=0; + level=0; + endgame=false; + for (i=0;i<NO_OF_CARDS;i++) played[i]=0; +} + +lgame::lgame(lgame &game) +{ + *this=game; +} + +lgame &lgame::operator=(lgame &game) +{ + int i; + currentplayer=game.currentplayer; + startplayer=game.startplayer; + trump=game.trump; + movenumber=game.movenumber; + score[0]=game.score[0]; + score[1]=game.score[1]; + curmove[0]=game.curmove[0]; + curmove[1]=game.curmove[1]; + for (i=0;i<NO_OF_CARDS;i++) played[i]=game.played[i]; + for (i=0;i<NO_OF_CARDS;i++) card[i]=game.card[i]; + for (i=0;i<16;i++) cardheight[i]=game.cardheight[i]; + endgame=game.endgame; + level=game.level; + return *this; +} + +int lgame::MakeMove(int c,int pos) +{ + int h; + curmove[currentplayer]=c; + h=GetHeight(currentplayer,pos); + if (currentplayer==startplayer) + { + movenumber++; + SetHeight(currentplayer,pos,h-1); + currentplayer=1-startplayer; + } + else + { + if (!LegalMove(curmove[startplayer],c)) return -1; + SetHeight(currentplayer,pos,h-1); + if (WonMove(curmove[startplayer],curmove[1-startplayer])) + { + // switch startplayer + startplayer=1-startplayer; + } + currentplayer=startplayer; + score[startplayer]+=CardValue(curmove[0]); + score[startplayer]+=CardValue(curmove[1]); + + if (movenumber==NO_OF_TILES) + { + endgame=true; + return 2; + } + } + return 1; +} + +void lgame::Init() +{ + int player,i,h,j,card; + // check what cards are played + for (player=0;player<2;player++) + { + for (i=0;i<8;i++) + { + h=GetHeight(player,i); + for (j=h;j<2;j++) + { + card=GetCard(player,i,j+1); + if (card>=0) played[card]=1; + } + } + } +} + +// Add value of both cards to the score of startplayer +void lgame::AddScore(int c1,int c2) +{ + score[startplayer]+=CardValue(c1); + score[startplayer]+=CardValue(c2); +} +// Switch the startplayer +void lgame::SwitchStartplayer() +{ + startplayer=1-startplayer; +} + +// pos=0..7, player=0..1 +void lgame::SetHeight(int player, int pos,int h) +{ + int i; + i=8*player+pos; + cardheight[i]=h; +} +bool lgame::LegalMove(int p1, int p2) +{ + CCOLOUR col1,col2,col3; + CCARD card1,card2,card3; + card1=(CCARD)((p1)/4); + col1=(CCOLOUR)((p1)%4); + card2=(CCARD)((p2)/4); + col2=(CCOLOUR)((p2)%4); + + // force trump colour + if (card1==Jack) col1=trump; + if (card2==Jack) col2=trump; + + // same colour always ok + if (col1==col2) return true; + + // Search for same colour + bool flag=true; + for (int i=0;i<8;i++) + { + int h,c; + h=GetHeight(1-startplayer,i); + if (h==0) continue; + c=GetCard(1-startplayer,i,h); + card3=(CCARD)((c)/4); + col3=(CCOLOUR)((c)%4); + if (card3==Jack) col3=trump; + + if (col3==col1) + { + flag=false; + break; + } + } + if (flag) return true; + + + return false; +} +int lgame::CardValue(int card) +{ + int card1; + + card1=card/4; + return cardvalues[card1]; +} +int lgame::WonMove(int c1,int c2) +{ + CCOLOUR col1,col2; + CCARD card1,card2; + + card1=(CCARD)((c1)/4); + col1=(CCOLOUR)((c1)%4); + card2=(CCARD)((c2)/4); + col2=(CCOLOUR)((c2)%4); + + // Two jacks + if (card1==Jack && card2==Jack) + { + if (col1<col2) return 0; + else return 1; + } + // One Jack wins always + if (card1==Jack) return 0; + if (card2==Jack) return 1; + + // higher one wins if same colour + if (col1==col2) + { + if (card1==Ten) + { + if (card2==Ace) return 1; + else return 0; + } + if (card2==Ten) + { + if (card1==Ace) return 0; + return 1; + } + + if ((int)card1<(int)card2) return 0; + return 1; + } + // trump wins + if (col1==trump) return 0; + if (col2==trump) return 1; + + // first one wins + return 0; + +} +// pos=0..7, height=2..1..(0 no card left), player=0..1 +int lgame::GetCard(int player, int pos,int height) +{ + int i; + if (height==0) return -1; + height=2-height; + + i=NO_OF_TILES*player+8*height+pos; + return card[i]; +} + + + +// pos=0..7, player=0..1 +int lgame::GetHeight(int player, int pos) +{ + int i; + i=8*player+pos; + return cardheight[i]; +} + +// Returns a value for the given side +// applies all rules +int lgame::Subvalue(int side) +{ + int sc,card; + int i,h,c; + CCOLOUR col1; + CCARD card1; + int trum1; + int jack1; + int havecol[4]; + bool haveten[4]; + bool haveace[4]; + bool havejack[4]; + + sc=0; + trum1=0; + for (i=0;i<4;i++) + { + havecol[i]=false; + haveten[i]=false; + haveace[i]=false; + havejack[i]=false; + } + jack1=0; + for (i=0;i<8;i++) + { + h=GetHeight(side,i); + c=GetCard(side,i,h); + if (c<0) continue; + + card1=(CCARD)((c)/4); + col1=(CCOLOUR)((c)%4); + + if (col1==trump) trum1++; + havecol[(int)col1]++; + if (card1==Ten) + { + haveten[(int)col1]=true; + } + else if (card1==Ace) + { + haveace[(int)col1]=true; + } + else if (card1==Jack) + { + havejack[(int)col1]=true; + jack1++; + } + if (col1!=trump) + { + if (card1==Seven) sc-=60; + if (card1==Eight) sc-=50; + if (card1==Nine) sc-=40; + if (card1==Queen) sc-=10; + } + } + for (i=0;i<4;i++) + { + if (havecol[i]==0 && i!=trump) sc+=1000; + if (havecol[i]>5) sc+=800; + + if (haveten[i]&&havecol[i]<2) + { + card=8*i+Ace; + if (!played[card] && !haveace[i]) sc-=2500; // free ten + } + if (haveace[i]) sc+=1500; // ace + if (havejack[i]) + { + if (trump==Grand) sc+=4000+300*(4-i); + else sc+=2700+100*(4-i); + } + } + // evaluate + sc+=trum1*2500; + if (trum1==0) sc-=7000; + else if (trum1==1) sc-=5000; + return sc; +} + +int lgame::Value(int player) +{ + int sc; + sc=0; + + // Someone won? + if (score[0]>90) sc+=90000; + else if (score[0]>60) sc+=70000; + else if (score[0]==60) sc+=40000; + + if (score[1]>90) sc-=90000; + else if (score[1]>60) sc-=70000; + else if (score[1]==60) sc-=40000; + + // Reward points + sc+=(score[0]-score[1])*650; + + // Calulate cards + sc+=Subvalue(0); + sc-=Subvalue(1); + + // random + sc+=random(500)-250; + + if (player==1) return -sc; + return sc; +} + + +// -------------class lskatproc ---------------------------- +lskatproc::lskatproc() + : KInputChildProcess(4096) +{ + + initrandom(); +} + +lskatproc::~lskatproc(){ +} + + + +bool lskatproc::ReceiveMsg(KEMessage* msg,int id) +{ +// time_t timee,timea; +short x,y; + + SendDebug("Receiv Msg"); + // end of process + if (msg->HasKey(QCString("Terminate"))) + { + Terminate(); + } + // Init of process + if (msg->HasKey(QCString("Init"))) + { + // No init necessary + } + // Make a move + if (msg->HasKey(QCString("Cards"))) + { + SendDebug("Process HasKey(Cards)"); + // new game object + lgame game; + // extract data from message + game.ExtractGame(msg); + game.Init(); // must be AFTER ExtractGame + + // Debug stuff only + sprintf(buf,"Trump=%d move=%d sc1=%d sc2=%d", + game.trump,game.curmove[1-game.currentplayer],game.score[0],game.score[1]); + SendDebug(buf); + + if (game.currentplayer==0 && game.startplayer==0) + sprintf(buf,"+++ Computer ACTS as player ONE\n"); + else if (game.currentplayer==0 && game.startplayer==1) + sprintf(buf,"+++ Computer REACTS as player ONE\n"); + else if (game.currentplayer==1 && game.startplayer==1) + sprintf(buf,"+++ Computer ACTS as player TWO\n"); + else + sprintf(buf,"+++ Computer REACTS as player TWO\n"); + SendDebug(buf); + + // fills data + x=0;y=0; + GetComputerMove(game,x,y,0); + sprintf(buf,"Computer move player=%d x=%d y=%d",game.currentplayer,x,y); + SendDebug(buf); + + + // report move + msg->RemoveAll(); + msg->AddData(QCString("Move"),game.currentplayer); + msg->AddData(QCString("MoveX"),x); + msg->AddData(QCString("MoveY"),y); + + //timee=time(0); + // Sleep a minimum amount to slow down moves + //if ( 1000*(timee-timea) < MIN_TIME) usleep((MIN_TIME-1000*(timee-timea))); + SendDebug("Sending move back to main"); + + if (!IsTerminated()) SendMsg(msg); + fflush(stdout); // I do not know why it is needed..send does it too? + } + + return true; +} + + +/* --------------------------------------------------------------------------- */ +/* Computer Routinen */ +/* --------------------------------------------------------------------------- */ + +// extract game from msg +int lgame::ExtractGame(KEMessage *msg) +{ + int i; + short tmp; + char *p; + int size; + + msg->GetData(QCString("Startplayer"),startplayer); + msg->GetData(QCString("CurrentPlayer"),currentplayer); + msg->GetData(QCString("Cards"),p,size); + msg->GetData(QCString("Level"),level); + level--; // start with level 0 + for (i=0;i<NO_OF_CARDS;i++) + { + card[i]=((int *)p)[i]; + } + msg->GetData(QCString("Height"),p,size); + for (i=0;i<NO_OF_TILES;i++) + { + cardheight[i]=((int *)p)[i]; + } + msg->GetData(QCString("Trump"),tmp); + trump=(CCOLOUR)tmp; + short mm; + msg->GetData(QCString("CurrentMove"),mm); + curmove[1-currentplayer]=(int)mm; + curmove[currentplayer]=-1; + msg->GetData(QCString("No"),movenumber); + msg->GetData(QCString("Sc1"),score[0]); + msg->GetData(QCString("Sc2"),score[1]); + return 1; +} + +long lgame::random(long max) +{ +double value; +int r; + r=rand(); + value=(double)((double)r*(double)max)/(double)RAND_MAX; + return (long)value; +} + +void lskatproc::initrandom() +{ + srand( (unsigned)time( NULL ) ); // randomize +} + + +int lskatproc::GetComputerMove(lgame game,short &x,short &y,int rek) +{ + int i,maxvalue,maxmove,h,c; + //short oldscore; + bool startflag; + int startplayer; + int value; + lgame cgame; + char sbuf[100]; + short mx,my; + + + for (i=0;i<2*rek;i++) sbuf[i]=' '; + sbuf[2*rek]=0; + + x=0; + y=0; + if (game.currentplayer==game.startplayer) startflag=true; + else startflag=false; + + startplayer=game.startplayer; + + maxmove=0; + maxvalue=LOWERT; + + sprintf(buf,"%s:Prepareing computer move (cur=%d) startflag=%d", + sbuf,game.currentplayer,startflag); + //SendDebug(buf); + for (i=0;i<8;i++) + { + sprintf(buf,"%s:Checking for card %d of player %d\n",sbuf,i,game.currentplayer); + // SendDebug(buf); + cgame=game; + h=cgame.GetHeight(cgame.currentplayer,i); + if (h<1) + { + sprintf(buf,"%s:i=%d:: no cards left",sbuf,i); + // SendDebug(buf); + continue; // no cards left + } + c=cgame.GetCard(cgame.currentplayer,i,h); + if (cgame.MakeMove(c,i)<0) + { + sprintf(buf,"%s:i=%d:: no legal move c1=%d c2=%d", + sbuf,i,cgame.curmove[cgame.startplayer],c); + // SendDebug(buf); + continue; // wrong card + } + if (!startflag) // we are second + { + sprintf(buf,"LEVEL %d %d",cgame.level,rek); + SendDebug(buf); + // Still recursion necessary and game not yet ended? + if (rek<2*cgame.level && !cgame.endgame) + { + // If we have the same startplayer the movesequence + // is not switched and we can take the negative value + // otherwise we play again, and have to take the poitiv value + if (cgame.startplayer==startplayer) + { + value=-GetComputerMove(cgame,mx,my,rek+1); + // if (value==-LOWERT) value=LOWERT; // no move possible + } + else + value=GetComputerMove(cgame,mx,my,rek+1); + } + else // evaluate position + { + value=cgame.Value(1-startplayer); + } + } + else // we are first player + { + // Alwayss the other player moves now + value=-GetComputerMove(cgame,mx,my,rek+1); + } + + sprintf(buf,"%s:i=%d:: Value=%d",sbuf,i,value); + SendDebug(buf); + + if (value>maxvalue) + { + maxvalue=value; + maxmove=i; + } + } + x=maxmove%4; + y=maxmove/4; + return maxvalue; +} + +void lskatproc::SendDebug(const char *s) +{ + KEMessage *msg=new KEMessage; + msg->AddData(QCString("Debug"),s); +// msg->AddData("KLogSendMsg","debug.log"); +// DEBUG +// SendMsg(msg); +// printf("%s\n",s); + + delete msg; +} diff --git a/lskat/lskatproc/lskatproc.h b/lskat/lskatproc/lskatproc.h new file mode 100644 index 00000000..b5840461 --- /dev/null +++ b/lskat/lskatproc/lskatproc.h @@ -0,0 +1,104 @@ +/*************************************************************************** + lskatproc.h - description + ------------------- + begin : Sun Apr 9 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef LSKATPROC_H +#define LSKATPROC_H +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <math.h> +#include <string.h> + +#include "KInputChildProcess.h" + +/** + *@author Martin Heni + */ + + + +#define LOWERT -999999999L +#define SIEG_WERT 9999999L + + +#define START_REK 1 // (0) 1:Nur Stellungsbewertung bei Level 1 + // 0:Level 1 schon eine Rekursion + +typedef enum {Club=0,Spade=1,Heart=2,Diamond=3,Grand=4} CCOLOUR; +typedef enum {Ace=0,King=1,Queen=2,Jack=3,Ten=4,Nine=5,Eight=6,Seven=7} CCARD; +#define NO_OF_CARDS 32 +#define NO_OF_TILES 16 +#define NO_OF_TRUMPS 5 + +class lgame +{ + public: + lgame(); + lgame(lgame &game); + lgame &operator=(lgame &game); + + int WonMove(int c1,int c2); + int CardValue(int card); + bool LegalMove(int p1, int p2); + void SetHeight(int player, int pos,int h); + int GetHeight(int player, int pos); + int GetCard(int player, int pos,int height); + int Value(int player); + void AddScore(int c1,int c2); + void SwitchStartplayer(); + int MakeMove(int c,int pos); + void Init(); + long random(long max); + int Subvalue(int side); + int ExtractGame(KEMessage *msg); + + short currentplayer; + short startplayer; + int card[NO_OF_CARDS]; + int cardheight[16]; + int cardvalues[14]; + short score[2]; + CCOLOUR trump; + short movenumber; + int curmove[2]; + bool endgame; + int played[NO_OF_CARDS]; // cards already played + short level; +}; + + +class lskatproc : public KInputChildProcess +{ + +private: + +public: + lskatproc(); + ~lskatproc(); + + virtual bool ReceiveMsg(KEMessage *msg,int id); + + + void initrandom(); + int GetComputerMove(lgame game,short &x,short &y,int rek); + void SendDebug(const char *s); + + private: + char buf[1024]; +}; + +#endif diff --git a/lskat/lskatproc/main.cpp b/lskat/lskatproc/main.cpp new file mode 100644 index 00000000..d25cd8c9 --- /dev/null +++ b/lskat/lskatproc/main.cpp @@ -0,0 +1,28 @@ +/*************************************************************************** + main.cpp - description + ------------------- + begin : Sun Apr 9 22:56:15 CEST 2000 + copyright : (C) 2000 by Martin Heni + email : martin@heni-online.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <stdio.h> +#include "lskatproc.h" + + + + +int main(int argc, char *argv[]) +{ + lskatproc mComm; + return mComm.exec() ? 0 : 1; +} diff --git a/lskat/lskatproc/templates/cpp_template b/lskat/lskatproc/templates/cpp_template new file mode 100644 index 00000000..6afef5d4 --- /dev/null +++ b/lskat/lskatproc/templates/cpp_template @@ -0,0 +1,16 @@ +/*************************************************************************** + |FILENAME| - description + ------------------- + begin : |DATE| + copyright : (C) |YEAR| by |AUTHOR| + email : |EMAIL| + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ diff --git a/lskat/lskatproc/templates/header_template b/lskat/lskatproc/templates/header_template new file mode 100644 index 00000000..6afef5d4 --- /dev/null +++ b/lskat/lskatproc/templates/header_template @@ -0,0 +1,16 @@ +/*************************************************************************** + |FILENAME| - description + ------------------- + begin : |DATE| + copyright : (C) |YEAR| by |AUTHOR| + email : |EMAIL| + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ |