aboutsummaryrefslogtreecommitdiff
path: root/signfs/libtools.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--signfs/libtools.c1126
1 files changed, 1126 insertions, 0 deletions
diff --git a/signfs/libtools.c b/signfs/libtools.c
new file mode 100644
index 0000000..3464bfc
--- /dev/null
+++ b/signfs/libtools.c
@@ -0,0 +1,1126 @@
+/*
+ * File: libtools.c
+ * Autors: Willy Tarreau <willy@ant-computing.com>
+ */
+
+/*
+ * libtools.c : fonctions utilisees par les differents outils
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+#include <sys/time.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "libtools.h"
+
+#define CONFPARSE ConfParse
+
+#ifdef CONFPARSE
+extern long int CONFPARSE(char *str);
+#endif
+
+static char *loglevel_names[] = LOGLEVEL_NAMES;
+static FILE *log = NULL;
+unsigned int LogLevel = 0;
+
+typedef struct s_varlist t_varlist;
+/* structure locale pour les variables */
+struct s_varlist {
+ char id[CONF_IDLEN];
+ char *val;
+ t_varlist *next;
+};
+
+static t_varlist varlist = { "", NULL, NULL };
+
+
+/* Arreter avec un ABORT apres avoir affiche un message d'erreur sur STDERR.
+ * Cette fonction est de type int pour pouvoir etre inseree dans une expression.
+ * Elle ne retourne jamais.
+ */
+int Abort(char *fmt, ...) {
+ va_list argp;
+ struct timeval tv;
+ struct tm *tm;
+
+ va_start(argp, fmt);
+
+ gettimeofday(&tv, NULL);
+ tm=localtime(&tv.tv_sec);
+ fprintf(stderr, "[ABT] %03d %02d%02d%02d pid=%d, cause=",
+ tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, getpid());
+ vfprintf(stderr, fmt, argp);
+ fflush(stderr);
+ va_end(argp);
+ abort();
+ return 0; // juste pour eviter un warning a la compilation
+}
+
+/* cette fonction extrait la valeur numérique correspondant à la chaine ASCII
+ * de <nb> chiffres à la position <from>
+ */
+unsigned long Ascii2Long(char *from, int nb) {
+ unsigned long ret=0;
+ while (nb-- && *from)
+ ret=ret*10+(*from++-'0');
+ return ret;
+}
+
+
+/* effectue un dump hexa de la structure pointee par <d>, a partir de la position
+ * <start> et pour <l> octets
+ */
+void LogDump(char *d, int start, int l) {
+ int i,j;
+ if (log == NULL)
+ Abort("Tentative d'ecriture dans un log non ouvert\n");
+
+ for (i=start & -16;i<start+l;i+=16) {
+ fprintf(log,"%04x: ",i);
+ for (j=0; j<16; j++) {
+ if ((i+j>=start) && (i+j<start+l))
+ fprintf(log,"%02x ",(unsigned char)d[i+j]);
+ else
+ fprintf(log,".. ");
+ if (j==7) fprintf(log,"- ");
+ }
+ fprintf(log," ");
+ for (j=0; j<16; j++) {
+ if ((i+j>=start) && (i+j<start+l) && isprint(d[i+j]))
+ fputc(d[i+j],log);
+ else
+ fputc('.',log);
+ }
+ fputc('\n',log);
+ }
+ fflush(log);
+}
+
+
+/* effectue un dump de la structure pointee par <d>, a partir de la position
+ * <start> et pour <l> octets
+ */
+void RawDump(char *d, int start, int l) {
+ if (log == NULL)
+ Abort("Tentative d'ecriture dans un log non ouvert\n");
+
+ fwrite(d+start,l,1,log);
+}
+
+/*
+ * ouvre un log vers le fichier <file> en niveau <lev>.
+ * Si <file> est NULL, le log est envoyé vers <stderr>.
+ * Le fichier est créé s'il n'existe pas. Dans les cas contraire, les données
+ * sont ajoutées à la fin.
+ * Pour le niveau de log, voir l'explication dans la fonction LogSetLevel()
+ * ci-dessous.
+ */
+void LogOpen(char *file, unsigned int lev) {
+ FILE *oldlog=log;
+
+ if (oldlog == stderr && file != NULL) {
+ int oldlevel;
+ oldlevel=LogSetLevel(LOG_LALL, LOG_LSTAT);
+ Log(LOG_LSTAT,"Redirection des logs vers le fichier <%s>.\n", file);
+ LogSetLevel(LOG_LNONE, oldlevel);
+ }
+ else if (oldlog != NULL && log != stderr) {
+ int oldlevel;
+ oldlevel=LogSetLevel(LOG_LALL, LOG_LSTAT);
+ Log(LOG_LWARN|LOG_LSTAT,"!!!!!!! Attention : reouverture du log dans le fichier <%s>.\n", file);
+ LogSetLevel(LOG_LNONE, oldlevel);
+ fclose(oldlog);
+ }
+ if (file == NULL || ((log=fopen(file, "a+")) == NULL))
+ log=stderr; /* si on ne peut pas ouvrir le log, on envoie sur stderr */
+
+ LogLevel = lev;
+
+ if (log != NULL && log != stderr) {
+ char logstr[32*4+1+1], *p;
+ int l,printed=0,oldlevel;
+ p=logstr;
+ *p++='[';
+ for (l=0; l<32; l++) {
+ if (LogLevel & (1<<l)) {
+ if (printed++)
+ *p++=',';
+ strncpy(p, loglevel_names[l], (logstr+sizeof(logstr)-p));
+ p+=strlen(p);
+ }
+ }
+ *p++=']'; *p++=0;
+
+ oldlevel=LogSetLevel(LOG_LALL, LOG_LSTAT);
+ Log(LOG_LSTAT,"########### ouverture du log, pid %d, %s %s ##############\n",
+ getpid(),
+ printed?(printed==1?"niveau":"niveaux"):"AUCUN NIVEAU ACTIF", printed?logstr:"");
+ LogSetLevel(LOG_LNONE, oldlevel);
+ }
+ if (file != NULL && log == stderr)
+ Log(LOG_LERR,"!!!!!!! Erreur sur l'ouverture du fichier de log <%s>.\n",file);
+}
+
+/* ferme le log */
+void LogClose() {
+ if (log != NULL && log != stderr)
+ fclose(log);
+}
+
+/* change le niveau de trace. Effectue un ET logique sur le niveau courant puis un OU logique.
+ * Exemples :
+ * LogSetLevel(0, LOGLSOCKET); => ne logue que les sockets
+ * LogSetLevel(-1, LOGLSOCKET); => ajoute les logs de sockets aux logs courants
+ * LogSetLevel( ~LOGLSOCKET, 0); => supprime les logs de sockets des logs courants
+ * LogSetLevel( ~LOGLMEMORY, LOGLFILE); => supprime les logs memoire et ajoute les logs fichiers
+ * On retourne le niveau précédent;
+ */
+int LogSetLevel(int and, int or) {
+ int oldlevel;
+ LogLevel = ((oldlevel=LogLevel) & and) | or;
+ return oldlevel;
+}
+
+/*
+ * envoie une trace au format de printf().
+ * Ce log n'est affiché que si le niveau passé en paramètre correspond au niveau courant.
+ * Si le niveau LOGLPID est actif, le pid est préfixé. Si le niveau LOGLDATE
+ * est actif, la date est préfixée.
+ * La sortie subit toujours un fflush() avant le retour.
+ * on retourne toujours 0, juste pour pouvoir inserer la fonction dans une expression.
+ */
+int Log(int level, const char *fmt, ...) {
+ va_list argp;
+ struct timeval tv;
+ struct tm *tm;
+ char logstr[2048], *p;
+
+ if ((level & (LogLevel | LOG_LFATAL)) == 0)
+ return 0;
+
+ *(p=logstr)='\0';
+ va_start(argp, fmt);
+ if (log) {
+ int l,printed=0;
+ *p++='[';
+ for (l=0; l<32; l++) {
+ if ((level & (LogLevel)) & (1<<l)) {
+ if (printed++)
+ *p++=',';
+ strncpy(p, loglevel_names[l], (logstr+sizeof(logstr)-p));
+ p+=strlen(p);
+ }
+ }
+ *p++=']';
+ gettimeofday(&tv, NULL);
+ tm=localtime(&tv.tv_sec);
+#ifdef HAVE_SNPRINTF
+ snprintf(p, (logstr+sizeof(logstr)-p), " %03d %02d%02d%02d.%03d ", tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec,
+ ((tv.tv_usec)/1000)%1000);
+#else
+ sprintf(p, " %03d %02d%02d%02d.%03ld ", tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, ((tv.tv_usec)/1000)%1000);
+#endif
+ p+=strlen(p);
+ }
+#ifdef HAVE_SNPRINTF
+ vsnprintf(p, (logstr+sizeof(logstr)-p), fmt, argp);
+#else
+ vsprintf(p, fmt, argp);
+#endif
+ write(fileno(log?log:stderr), logstr, strlen(logstr));
+ va_end(argp);
+ return 0;
+}
+
+
+/**************************************************************\
+ *
+ * Outils de gestion de configuration
+ *
+\**************************************************************/
+
+
+/* recherche d'une variable par son nom d'identificateur.
+ Le pointeur sur la structure est donné en retour. Si le nom n'est pas trouvé,
+ NULL est retourné.
+*/
+
+static t_varlist *ptrvar(char *name) {
+ t_varlist *p;
+
+ p=varlist.next;
+ while (p) {
+ if (!strcmp(p->id,name))
+ return p;
+ p=p->next;
+ }
+ return NULL;
+}
+
+/*
+ * Lecture du fichier de configuration dont le nom est passé dans <name>.
+ * Le contenu de ce fichier est ajouté à la configuration existante.
+ * On retourne le nombre d'erreurs rencontrées.
+ */
+int ConfRead(char *name) {
+ char *p;
+ FILE *cnf;
+ char ligne[CONF_LINELEN];
+ char tmpval[256];
+ int err=0;
+
+ if ((cnf=fopen(name, "r"))==NULL) {
+ Log(LOG_LWARN, "Impossible d'ouvrir le fichier de configuration <%s>.\n",name);
+ return -1;
+ }
+
+ while (ConfReadLine(cnf, ligne, CONF_LINELEN)) {
+ if ((p=strchr(ligne,'=')) != NULL) {
+ *p++=0;
+ ConfDvlpStr(tmpval,p,sizeof(tmpval));
+ ConfSetStr(ligne,tmpval);
+ }
+ else {
+ Log(LOG_LERR, "Erreur de syntaxe dans le fichier de configuration <%s>, près de <%s>.\n", name, ligne);
+ err++;
+ }
+ }
+ fclose(cnf);
+ return err;
+}
+
+
+/* lecture d'une ligne NON VIDE dans le fichier <file>. Celle-ci est
+ stockée dans <ligne>, d'une longueur maximale <len>, et la valeur
+ retournée est le pointeur sur cette chaine. A la fin du fichier,
+ <ligne> est vidée et la valeur retournée est NULL.
+ Les lignes commencant par ';' ou '#' ne sont pas retournées.
+*/
+char *ConfReadLine(FILE *file, char *ligne, int len) {
+ char *ret;
+
+ /* recherche la prochaine ligne non vide */
+ do {
+ if ((ret=fgets(ligne, len, file)) == NULL)
+ break;
+ /* passe tous les premiers séparateurs */
+ while (/* *ret &&*/ (*ret==' ' || *ret == '\t' || *ret=='\n' || *ret=='\r'))
+ ret++;
+ if (*ret=='#' || *ret==';')
+ *ret=0; /* marque la fin immédiate */
+ } while (!*ret);
+
+ if (!ret) { /* fin de fichier */
+ *ligne='\0';
+ return NULL;
+ }
+
+ /* suppression des sauts de lignes et retours inutiles en fin de ligne */
+ ret=ligne+strlen(ligne)-1;
+ while (ret>=ligne && (*ret=='\r' || *ret=='\n'))
+ *ret--=0;
+
+ return ligne;
+}
+
+
+/* affecte la valeur <val> à la variable <id>.
+ * Si la variable n'existe pas, elle est créée.
+ */
+void ConfSetStr(char *id, char *val) {
+ t_varlist *p;
+
+ if ((p=ptrvar(id)) == NULL) { /* nouvelle variable */
+ if ((p=malloc(sizeof(t_varlist)))==NULL)
+ Abort("setvar():malloc()");
+ strcpy0(p->id,id);
+ p->val=NULL;
+ /* insertion dans la liste */
+ p->next=varlist.next;
+ varlist.next=p;
+ }
+ if (p->val) free(p->val);
+ p->val=(char *)malloc(strlen(val)+1);
+ strcpy(p->val,val);
+}
+
+/* affecte la valeur entiere <val> à la variable <id>.
+ * Si la variable n'existe pas, elle est créée.
+ */
+void ConfSetInt(char *id, long int val) {
+ char entier[32];
+ sprintf(entier,"%ld",val);
+ ConfSetStr(id,entier);
+}
+
+/* retourne la valeur de la variable ou de la var d'environnement ou "" */
+char *ConfGetStr(char *id) {
+ t_varlist *p;
+ char *v;
+
+ /*printf("demande de la variable <%s> : ",id);*/
+ if ((p=ptrvar(id)) != NULL)
+ v=p->val;
+ else if (!(v=getenv(id))) {
+ v="";
+ }
+ return v;
+}
+
+/* retourne la valeur entiere de la variable (interne ou env) ou 0 */
+long int ConfGetInt(char *id) {
+#ifdef CONFPARSE
+ return CONFPARSE(ConfGetStr(id));
+#else
+ return atol(ConfGetStr(id));
+#endif
+}
+
+/* développe une chaine de caractères contenant des variables sous la forme $(var) en une
+ chaine dans laquelle les variables sont remplacées par leur valeur. La chaine de résultat
+ fera au plus <lmax> caractères.
+*/
+
+char *ConfDvlpStr(char *out, char *in, int lmax) {
+ char *res;
+ char *p1, *p2;
+ char var[CONF_LINELEN];
+ char tmpvar[CONF_LINELEN];
+ int pardepth;
+
+ res=out;
+ while (lmax && *in) {
+ while (lmax && *in && (*in!='$'))
+ *out++=*in++;
+ if (*in=='$') {
+ switch (*++in) {
+ case '$' : /* certains caractères sont réservés. C'est donc un code escape */
+ if (lmax) {
+ *out++=*in++;
+ lmax--;
+ }
+ break;
+ case '(' : /* parenthese : c'est une variable */
+ p1=++in;
+ pardepth=1;
+ while (*in && ((*in != ')') || (pardepth>1)) && (in-p1+1<sizeof(var))) {
+ if (*in == ')')
+ pardepth--;
+ else if (*in == '(')
+ pardepth++;
+ in++;
+ }
+ strncpy0(var,p1,in-p1); /* verifier la longueur .... */
+ /*var[in-p1]=0;*/
+ p2=ConfGetStr(ConfDvlpStr(tmpvar,var,sizeof(tmpvar))); /* le nom de la variable peut etre variable lui-meme */
+ if (strlen(p2)>lmax) {
+ strncpy(out,p2,lmax);
+ *(out+=lmax)=0;
+ }
+ else {
+ strcpy(out,p2);
+ *(out+=strlen(p2))=0;
+ }
+ if (*in) in++; /* passe la parenthese fermante */
+ break;
+ case '{' : /* accolade : commande */
+ p1=++in;
+ while (*in && (*in!='}') && (in-p1+1<sizeof(var)))
+ in++;
+ strncpy0(var,p1,in-p1); /* verifier la longueur .... */
+ /*var[in-p1]=0;*/
+ /* mettre dans p2 le resultat de la commande nommée par <var> */
+ p2=ConfGetStr(var);
+ if (strlen(p2)>lmax) {
+ strncpy(out,p2,lmax);
+ *(out+=lmax)=0;
+ }
+ else {
+ strcpy(out,p2);
+ *(out+=strlen(p2))=0;
+ }
+ if (*in) in++; /* passe l'accolade fermante */
+ break;
+ default : /* autre: non traite */
+ if (lmax) { *out++='$'; lmax--; }
+ if (lmax) { *out++=*in; lmax--; }
+ }
+ }
+ }
+ *out=0;
+ return res;
+}
+
+
+/********************************************************************
+ * fonctions d'analyse et de résolution d'expressions arithmétiques *
+ ********************************************************************/
+
+
+#define PARSESTR ConfGetStr
+
+#define NEXTCHAR(a) ((*a)++)
+#define PREVCHAR(a) ((*a)--)
+
+#ifdef PARSESTR
+extern char *PARSESTR(char *);
+#endif
+
+enum {
+ OP_SL=1, OP_LE, OP_LT,
+ OP_SR, OP_GE, OP_GT,
+ OP_EQ, OP_LG,
+ OP_NE, OP_NL, OP_NG,
+ OP_QN, OP_CO,
+ OP_BO, OP_LO,
+ OP_BA, OP_LA,
+ OP_BX, OP_LX,
+ OP_PW, OP_MU, OP_DI,
+ OP_MI, OP_PL,
+ OP_NO, OP_CP
+};
+
+static int expression(char **arg);
+
+#ifndef HAVE_INTPOW
+static int intpow(a,b) {
+ int c=1;
+ if (b>0) {
+ while (b--)
+ c*=a;
+ return c;
+ }
+ else if (b==0)
+ return 1;
+ else
+ return a==1; /* (1/a^n = 1) if (a==1), else 0 */
+}
+#endif
+
+static int readchar(char **arg, int c) {
+ if (c==**arg) {
+ NEXTCHAR(arg);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static int readalnum(char **arg) {
+ if (isalnum(**arg) || (**arg=='_') || (**arg=='.')) {
+ NEXTCHAR(arg);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static int readnum(char **arg) {
+ if (isdigit(**arg)) {
+ NEXTCHAR(arg);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static int readspace(char **arg) {
+ if (isspace(**arg)) {
+ NEXTCHAR(arg);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static int readany(char **arg) {
+ if (**arg==0)
+ return 0;
+ NEXTCHAR(arg);
+ return 1;
+}
+
+static int readeol(char **arg) {
+ if (**arg=='\n') {
+ NEXTCHAR(arg);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* skips all consecutive comments and spaces. returns no-zero if at least one comment was read */
+static int skipcomments(char **arg) {
+ int comm=0;
+ while (readspace(arg));
+ while (**arg=='#') {
+ while (!readeol(arg))
+ readany(arg);
+ while (readspace(arg));
+ comm=1;
+ }
+ return comm;
+}
+
+/* returns the work read */
+static char *readword(char **arg) {
+ static char token[100];
+ int tklen=0;
+
+ skipcomments(arg);
+ while (readalnum(arg) && (tklen<sizeof(token)-1))
+ token[tklen++]=*(*arg-1);
+ token[tklen]=0;
+ return token;
+}
+
+static int fetch(char **arg, int c) {
+ skipcomments(arg);
+ return readchar(arg,c);
+}
+
+/* returns 1 if the token can be fetched, otherwise 0. */
+static int fetchstr(char **arg, char *tok) {
+ char *newarg=*arg;
+ while (*tok && *newarg==*tok) {
+ newarg++;
+ tok++;
+ }
+ /* not everything matched */
+ if (*tok)
+ return 0;
+ /* everything matched so let's update arg */
+ *arg=newarg;
+ return 1;
+}
+
+
+/* returns 1 if the operator can be fetched, otherwise 0. */
+static int fetchop(char **arg, int op) {
+ char *oldarg=*arg;
+ int ret;
+ /* it's important to match first the longest operators to avoid prefixes collision */
+
+ if (fetch(arg,'+'))
+ ret = op==OP_PL;
+ else if (fetch(arg,'/'))
+ ret = op==OP_DI;
+ else if (fetch(arg,'-'))
+ ret = op==OP_MI;
+ else if (fetch(arg,'~'))
+ ret = op==OP_CP;
+ else if (fetch(arg,'?'))
+ ret = op==OP_QN;
+ else if (fetch(arg,':'))
+ ret = op==OP_CO;
+ else if (fetch(arg,'<')) { /* <<, <=, <>, < */
+ if (fetch(arg,'='))
+ ret = op==OP_LE;
+ else if (fetch(arg,'<'))
+ ret = op==OP_SL;
+ else if (fetch(arg,'>'))
+ ret = op==OP_LG;
+ else
+ ret = op==OP_LT;
+ }
+ else if (fetch(arg,'>')) { /* >>, >=, > */
+ if (fetch(arg,'='))
+ ret = op==OP_GE;
+ else if (fetch(arg,'>'))
+ ret = op==OP_SR;
+ else
+ ret = op==OP_GT;
+ }
+ else if (fetch(arg,'!')) { /* !<, !>, !=, ! */
+ if (fetch(arg,'<'))
+ ret = op==OP_NL;
+ else if (fetch(arg,'>'))
+ ret = op==OP_NG;
+ else if (fetch(arg,'='))
+ ret = op==OP_NE;
+ else
+ ret = op==OP_NO;
+ }
+ else if (fetch(arg,'|')) { /* ||, | */
+ if (fetch(arg,'|'))
+ ret = op==OP_BO;
+ else
+ ret = op==OP_LO;
+ }
+ else if (fetch(arg,'^')) { /* ^^,^ */
+ if (fetch(arg,'^'))
+ ret = op==OP_BX;
+ else
+ ret = op==OP_LX;
+ }
+ else if (fetch(arg,'&')) { /* &&, & */
+ if (fetch(arg,'&'))
+ ret = op==OP_BA;
+ else
+ ret = op==OP_LA;
+ }
+ else if (fetch(arg,'*')) { /* **, * */
+ if (fetch(arg,'*'))
+ ret = op==OP_PW;
+ else
+ ret = op==OP_MU;
+ }
+ else if (fetchstr(arg,"=="))
+ ret = op==OP_EQ;
+ else
+ ret = 0;
+
+ if (!ret)
+ *arg=oldarg; /* operator not matched */
+ return ret;
+}
+
+static int terme(char **arg) {
+ int a;
+ if (fetchop(arg, OP_MI))
+ a=-terme(arg);
+ else if (fetchop(arg, OP_CP))
+ a=~terme(arg);
+ else {
+ if (fetch(arg, '(')) {
+ a=expression(arg);
+ fetch(arg, ')');
+ }
+ else if (isdigit(**arg))
+ a=atol(readword(arg));
+ else
+#ifdef PARSESTR
+ a=ConfParse(PARSESTR(readword(arg)));
+#else
+ a=0;
+#endif
+
+ if (fetchop(arg, OP_PW)) /* exponentiation */
+ a=intpow(a,terme(arg));
+ }
+ return a;
+}
+
+static int produit(char **arg) {
+ int a,b;
+ a=terme(arg);
+ do {
+ if (fetchop(arg, OP_MU)) {
+ a*=terme(arg);
+ }
+ else if (fetchop(arg, OP_DI)) {
+ b=terme(arg);
+ if (b)
+ a/=b;
+ else
+ a=0; /* dividing anything by 0 will always return 0 */
+ }
+ else
+ break;
+ } while (1);
+ return a;
+}
+
+
+static int shifter(char **arg) {
+ int a;
+ a=produit(arg);
+ do {
+ if (fetchop(arg, OP_SL))
+ a<<=produit(arg);
+ else if (fetchop(arg, OP_SR))
+ a>>=produit(arg);
+ else
+ break;
+ } while (1);
+ return a;
+}
+
+static int log_conj(char **arg) {
+ int a;
+ a=shifter(arg);
+ while (fetchop(arg, OP_LA))
+ a&=shifter(arg);
+ return a;
+}
+
+static int log_disj(char **arg) {
+ int a;
+ a=log_conj(arg);
+ do {
+ if (fetchop(arg, OP_LO))
+ a|=log_conj(arg);
+ else if (fetchop(arg, OP_LX))
+ a^=log_conj(arg);
+ else
+ break;
+ } while (1);
+ return a;
+}
+
+static int somme(char **arg) {
+ int a;
+ a=log_disj(arg);
+ do {
+ if (fetchop(arg, OP_PL))
+ a+=log_disj(arg);
+ else if (fetchop(arg, OP_MI))
+ a-=log_disj(arg);
+ else
+ break;
+ } while (1);
+ return a;
+}
+
+static int comp(char **arg) {
+ int a;
+ a=somme(arg);
+ do {
+ if (fetchop(arg, OP_EQ))
+ a=(a==somme(arg));
+ else if (fetchop(arg, OP_LE) || fetchop(arg, OP_NG))
+ a=(a<=somme(arg));
+ else if (fetchop(arg, OP_GE) || fetchop(arg, OP_NL))
+ a=(a>=somme(arg));
+ else if (fetchop(arg, OP_LG) || fetchop(arg, OP_NE))
+ a=(a!=somme(arg));
+ else if (fetchop(arg, OP_LT))
+ a=(a<somme(arg));
+ else if (fetchop(arg, OP_GT))
+ a=(a>somme(arg));
+ else
+ break;
+ } while (1);
+ return a;
+}
+
+static int boolean(char **arg) {
+ if (fetchop(arg, OP_NO))
+ return !boolean(arg);
+ else
+ return comp(arg);
+}
+
+
+static int bool_conj(char **arg) {
+ int a;
+ a=boolean(arg);
+ while (fetchop(arg, OP_BA))
+ a = a && boolean(arg);
+ return a;
+}
+
+static int bool_disj(char **arg) {
+ int a;
+ a=bool_conj(arg);
+ do {
+ if (fetchop(arg, OP_BO))
+ a = a || bool_conj(arg);
+ // else if (fetchop(arg, OP_BX))
+ // a = a ^^ bool_conj(arg);
+ else
+ break;
+ } while (1);
+ return a;
+}
+
+
+static int expression(char **arg) {
+ int cond, exp1, exp2;
+ cond=bool_disj(arg);
+ if (fetchop(arg, OP_QN)) {
+ if (fetchop(arg, OP_CO)) { /* missing expr means keep cond if true */
+ exp1=cond;
+ exp2=expression(arg);
+ }
+ else {
+ exp1=expression(arg);
+ if (fetchop(arg, OP_CO))
+ exp2=expression(arg);
+ else
+ exp2=0; /* missing colon means 0 */
+ }
+ return cond ? exp1 : exp2;
+ }
+ else
+ return cond;
+}
+
+/**** la seule fonction exportée ****/
+long int ConfParse(char *arg) {
+ return expression(&arg);
+}
+#undef PARSESTR
+#undef NEXTCHAR
+#undef PREVCHAR
+
+
+/**********************************************************
+ * fonctions de traitements sur les chaines de caractères *
+ **********************************************************/
+
+/* strncpy avec 0 final. src peut etre NULL.
+ Attention: <lmax> caracteres sont copiés, et un zéro terminal est
+ ajouté APRES. <dest> doit donc pouvoir accepter lmax+1 caractères.
+*/
+
+char *strncpy0(char *dest, const char *src, int lmax) {
+ register char *p;
+ p=dest;
+ if (src)
+ while (lmax-- && (*p++=*src++));
+ *p=0;
+ return dest;
+}
+
+
+/* retourne un pointeur sur la chaine S tronquée à l caractères maximum */
+char *strcut(char *s, int l) {
+ char *p=s;
+
+ while (*p && l) {
+ p++; l--;
+ }
+ *p=0;
+ return s;
+}
+
+static char base64tab[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int char64to6bits(char c) {
+ return (c=='/'?63:
+ c=='+'?62:
+ (c>='0' && c<='9')?(c-'0'+52):
+ (c>='a' && c<='z')?(c-'a'+26):
+ (c>='A' && c<='Z')?(c-'A'+0):
+ 0);
+}
+
+/* WARNING !!! no boundary checking is done. The caller *MUST* have enough
+ * space to store the result : outsize = (insize+3)*4/3
+ */
+char *asciitobase64(char *ascii, char *base64) {
+ char *dest=base64;
+ while (ascii[0] && ascii[1] && ascii[2]) {
+ *dest++=base64tab[(ascii[0]>>2) & 0x3f];
+ *dest++=base64tab[(ascii[0] & 0x03)<<4 | ((ascii[1] >> 4) & 0x0f)];
+ *dest++=base64tab[(ascii[1] & 0x0f)<<2 | ((ascii[2] >> 6) & 0x03)];
+ *dest++=base64tab[(ascii[2] & 0x3f)];
+ ascii+=3;
+ }
+ /* only 0, 1 or 2 bytes left */
+ if (*ascii) { /* 1 or 2 bytes left */
+ *dest++=base64tab[(ascii[0]>>2) & 0x3f];
+ if (!ascii[1]) /* 1 byte left */
+ *dest++=base64tab[(ascii[0] & 0x03)<<4];
+ else { /* 2 bytes left */
+ *dest++=base64tab[(ascii[0] & 0x03)<<4 | ((ascii[1] >> 4) & 0x0f)];
+ *dest++=base64tab[(ascii[1] & 0x0f)<<2];
+ }
+ *dest++='=';
+ }
+ *dest++=0;
+ return base64;
+}
+
+/* WARNING !!! no boundary checking is done. The caller *MUST* have enough
+ * space to store the result : outsize = (insize+2)/3*4 + 1 (zero)
+ */
+char *asciintobase64(char *ascii, int len, char *base64) {
+ char *dest=base64;
+
+ while (len>2) {
+ *dest++=base64tab[((unsigned)ascii[0]>>2) & 0x3f];
+ *dest++=base64tab[((unsigned)ascii[0] & 0x03)<<4 | (((unsigned)ascii[1] >> 4) & 0x0f)];
+ *dest++=base64tab[((unsigned)ascii[1] & 0x0f)<<2 | (((unsigned)ascii[2] >> 6) & 0x03)];
+ *dest++=base64tab[((unsigned)ascii[2] & 0x3f)];
+ ascii+=3;
+ len-=3;
+ }
+
+ /* only 0, 1 or 2 bytes left */
+ if (len>0) { /* 1 or 2 bytes left */
+ *dest++=base64tab[((unsigned)ascii[0]>>2) & 0x3f];
+ if (len==1) /* 1 byte left */
+ *dest++=base64tab[((unsigned)ascii[0] & 0x03)<<4];
+ else { /* 2 bytes left */
+ *dest++=base64tab[((unsigned)ascii[0] & 0x03)<<4 | (((unsigned)ascii[1] >> 4) & 0x0f)];
+ *dest++=base64tab[((unsigned)ascii[1] & 0x0f)<<2];
+ }
+ *dest++='=';
+ }
+ *dest++=0;
+ return base64;
+}
+
+/* WARNING !!! no boundary checking is done. The caller *MUST* have enough
+ * space to store the result : outsize = insize*3/4
+ * an empty string correctly returns an empty string.
+ */
+char *base64toascii(char *ascii, char *base64) {
+ int i,j;
+ char *p=ascii;
+
+ do {
+ for (i=4,j=0;i>0;i--) {
+ if (*base64=='=' || *base64==0) /* fin */ {
+ j<<=(6*i);
+ break;
+ }
+ j=(j<<6)|char64to6bits(*base64++);
+ }
+ if (i!=4) { /* au moins un caractere lu ? */
+ *p++=(j>>16)&255;
+ if (i!=2) { /* le deuxieme octet est renseigne */
+ *p++=(j>>8)&255;
+ if (i!=1) { /* le troisieme octet est renseigne */
+ *p++=(j)&255;
+ }
+ }
+ }
+ } while (!i); /* tant que i est nul, on a lu 4 caracteres donc 3 octets */
+ *p++=0; /* fin de chaine forcee */
+ return ascii;
+}
+
+/* reads a string of the form "var=val; var=val; ..." and extracts the desired
+ "var" specified in <token>. The corresponding "val" is copied into <to> for
+ at most <len> chars, including the ending 0. The result is <to>, or NULL if
+ token not found, and <to> is set to an empty string.
+*/
+char *extracttoken(const char *from, const char *token, char *to, int len) {
+ char *s;
+ s=(char *)from;
+ *to=0;
+ while (1) {
+ if ((s=strstr(s, token)) == NULL)
+ return NULL;
+ s+=strlen(token);
+ if (*s != '=') {
+ while(*s && !isspace(*s) && (*s!=';'))
+ s++;
+ while(*s && (isspace(*s) || (*s!=';')))
+ s++;
+ }
+ else {
+ char *t=to;
+ s++;
+ while(*s && !isspace(*s) && (*s!=';') && len-->1)
+ *t++=*s++;
+ *t++=0;
+ return to;
+ }
+ }
+}
+
+/*
+ * calculations on timeval structs. Integers are milliseconds.
+ */
+
+/* sets <tv> to the current time */
+struct timeval *tv_now(struct timeval *tv) {
+ if (tv)
+ gettimeofday(tv, NULL);
+ return tv;
+}
+
+/* returns a statically allocated struct timeval initialized with <ms> ms */
+struct timeval *tv_ms(int ms) {
+ static struct timeval tv;
+ tv.tv_usec = (ms%1000)*1000;
+ tv.tv_sec = (ms/1000);
+ return &tv;
+}
+
+/* adds <ms> ms to tv and returns a pointer to the newly filled struct */
+struct timeval *tv_delay(struct timeval *tv, int ms) {
+ if (!tv)
+ return NULL;
+ tv->tv_usec += (ms%1000)*1000;
+ tv->tv_sec += (ms/1000);
+ if (tv->tv_usec >= 1000000) {
+ tv->tv_usec -= 1000000;
+ tv->tv_sec++;
+ }
+ return tv;
+}
+
+/* adds <ms> ms to <from>, set the result to <tv> and returns a pointer <tv> */
+struct timeval *tv_delayfrom(struct timeval *tv, struct timeval *from, int ms) {
+ if (!tv || !from)
+ return NULL;
+ tv->tv_usec = from->tv_usec + (ms%1000)*1000;
+ tv->tv_sec = from->tv_sec + (ms/1000);
+ while (tv->tv_usec >= 1000000) {
+ tv->tv_usec -= 1000000;
+ tv->tv_sec++;
+ }
+ return tv;
+}
+
+/* copies <from> to <tv> and returns a pointer <tv> */
+struct timeval *tv_set(struct timeval *tv, struct timeval *from) {
+ if (!tv || !from)
+ return NULL;
+ tv->tv_usec = from->tv_usec;
+ tv->tv_sec = from->tv_sec;
+ return tv;
+}
+
+/* sets tv to now + <ms> ms, and returns a pointer to the newly filled struct */
+struct timeval *tv_wait(struct timeval *tv, int ms) {
+ if (!tv)
+ return NULL;
+ gettimeofday(tv, NULL);
+ tv_delay(tv, ms);
+ return tv;
+}
+
+/* compares <tv1> and <tv2> : returns 0 if equal, -1 if tv1 < tv2, 1 if tv1 > tv2 */
+int tv_cmp(struct timeval *tv1, struct timeval *tv2) {
+ if (tv1->tv_sec > tv2->tv_sec)
+ return 1;
+ else if (tv1->tv_sec < tv2->tv_sec)
+ return -1;
+ else if (tv1->tv_usec > tv2->tv_usec)
+ return 1;
+ else if (tv1->tv_usec < tv2->tv_usec)
+ return -1;
+ else return 0;
+}
+
+/* returns 1 if <tv> is in the past (means tv<=gettimeofday()) or 0 if not) */
+int tv_past(struct timeval *tv) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ if ( tv_cmp(tv, &now) <= 0 )
+ return 1;
+ else
+ return 0;
+}
+
+/* returns the absolute difference, in ms, between tv1 and tv2 */
+unsigned long tv_delta(struct timeval *tv1, struct timeval *tv2) {
+ int cmp;
+ unsigned long ret;
+
+
+ cmp=tv_cmp(tv1, tv2);
+ if (!cmp)
+ return 0; /* same dates, null diff */
+ else if (cmp<0) {
+ struct timeval *tmp=tv1;
+ tv1=tv2;
+ tv2=tmp;
+ }
+ ret=(tv1->tv_sec - tv2->tv_sec)*1000;
+ if (tv1->tv_usec > tv2->tv_usec)
+ ret+=(tv1->tv_usec - tv2->tv_usec)/1000;
+ else
+ ret-=(tv2->tv_usec - tv1->tv_usec)/1000;
+ return (unsigned long) ret;
+}