XrdOucStream.cc

Go to the documentation of this file.
00001 /******************************************************************************/
00002 /*                                                                            */
00003 /*                       X r d O u c S t r e a m . c c                        */
00004 /*                                                                            */
00005 /* (c) 2004 by the Board of Trustees of the Leland Stanford, Jr., University  */
00006 /*       All Rights Reserved. See XrdInfo.cc for complete License Terms       */
00007 /*   Produced by Andrew Hanushevsky for Stanford University under contract    */
00008 /*              DE-AC03-76-SFO0515 with the Deprtment of Energy               */
00009 /******************************************************************************/
00010 
00011 #include <ctype.h>
00012 #include <errno.h>
00013 #include <fcntl.h>
00014 #include <stdlib.h>
00015 #include <string.h>
00016 #include <stdio.h>
00017 #ifndef WIN32
00018 #include <poll.h>
00019 #include <unistd.h>
00020 #include <strings.h>
00021 #if !defined(__linux__) && !defined(__CYGWIN__)
00022 #ifdef __FreeBSD__
00023 #include <sys/param.h>
00024 #endif
00025 #include <sys/conf.h>
00026 #endif
00027 #include <sys/stat.h>
00028 #include <sys/termios.h>
00029 #include <sys/types.h>
00030 #include <sys/wait.h>
00031 #else // WIN32
00032 #include "XrdSys/XrdWin32.hh"
00033 #include <process.h>
00034 #endif // WIN32
00035 
00036 #include "XrdOuc/XrdOucEnv.hh"
00037 #include "XrdOuc/XrdOucStream.hh"
00038 #include "XrdOuc/XrdOucUtils.hh"
00039 #include "XrdSys/XrdSysHeaders.hh"
00040 #include "XrdSys/XrdSysLogger.hh"
00041 #include "XrdSys/XrdSysPlatform.hh"
00042 
00043 /******************************************************************************/
00044 /*                         l o c a l   d e f i n e s                          */
00045 /******************************************************************************/
00046   
00047 #define MaxARGC 64
00048 #define XrdOucStream_EOM  0x01
00049 #define XrdOucStream_BUSY 0x02
00050 
00051 #define Erq(p, a, b) Err(p, a, b, (char *)0)
00052 #define Err(p, a, b, c) (ecode=(Eroute ? Eroute->Emsg(#p, a, b, c) : a), -1)
00053 #define Erp(p, a, b, c)  ecode=(Eroute ? Eroute->Emsg(#p, a, b, c) : a)
00054 
00055 // The following is used by child processes prior to exec() to avoid deadlocks
00056 //
00057 #define Erx(p, a, b) if (Eroute) cerr <<#p <<' ' <<strerror(a) <<' ' <<b <<endl;
00058 
00059 /******************************************************************************/
00060 /*               o o u c _ S t r e a m   C o n s t r u c t o r                */
00061 /******************************************************************************/
00062   
00063 XrdOucStream::XrdOucStream(XrdSysError *erobj, const char *ifname,
00064                            XrdOucEnv   *anEnv, const char *Pfx)
00065 {
00066  char *cp;
00067      if (ifname)
00068         {myInst = strdup(ifname);
00069          if (!(cp = index(myInst, ' '))) {cp = myInst; myExec = 0;}
00070             else {*cp = '\0'; cp++;
00071                   myExec = (*myInst ? myInst : 0);
00072                  }
00073          if ((myHost = index(cp, '@')))
00074             {*myHost = '\0';
00075              myHost++;
00076              myName = (*cp ? cp : 0);
00077             } else {myHost = cp; myName = 0;}
00078         } else myInst = myHost = myName = myExec = 0;
00079 
00080      FD     = -1;
00081      FE     = -1;
00082      bsize  = 0;
00083      buff   = 0;
00084      bnext  = 0;
00085      bleft  = 0;
00086      recp   = 0;
00087      token  = 0;
00088      flags  = 0;
00089      child  = 0;
00090      ecode  = 0;
00091      notabs = 0;
00092      xcont  = 1;
00093      xline  = 0;
00094      Eroute = erobj;
00095      myEnv  = anEnv;
00096      sawif  = 0;
00097      skpel  = 0;
00098      if (myEnv && Eroute)
00099         {llBuff = (char *)malloc(llBsz);
00100          llBcur = llBuff; llBok = 0; llBleft = llBsz; *llBuff = '\0';
00101          Verbose= 1;
00102         } else {
00103          Verbose= 0;
00104          llBuff = 0;
00105          llBcur = 0;
00106          llBleft= 0;
00107          llBok  = 0;
00108         }
00109      varVal = (myEnv ? new char[maxVLen+1] : 0);
00110      llPrefix = Pfx;
00111 }
00112 
00113 /******************************************************************************/
00114 /*                                A t t a c h                                 */
00115 /******************************************************************************/
00116 
00117 int XrdOucStream::AttachIO(int infd, int outfd, int bsz)
00118 {
00119     if (Attach(infd, bsz)) return -1;
00120     FE = outfd;
00121     return 0;
00122 }
00123   
00124 int XrdOucStream::Attach(int FileDescriptor, int bsz) 
00125 {
00126 
00127     // Close the current stream. Close will handle unopened streams.
00128     //
00129     Close();
00130 
00131     // Allocate a new buffer for this stream
00132     //
00133     if (!bsz) buff = 0;
00134        else if (!(buff = (char *)malloc(bsz+1)))
00135                return Erq(Attach, errno, "allocate stream buffer");
00136 
00137     // Initialize the stream
00138     //
00139     FD= FE = FileDescriptor;
00140     bnext  = buff;
00141     bsize  = bsz+1;
00142     bleft  = 0;
00143     recp   = 0;
00144     token  = 0;
00145     flags  = 0;
00146     ecode  = 0;
00147     xcont  = 1;
00148     xline  = 0;
00149     sawif  = 0;
00150     skpel  = 0;
00151     if (llBuff) 
00152        {llBcur = llBuff; *llBuff = '\0'; llBleft = llBsz; llBok = 0;}
00153     return  0;
00154 }
00155   
00156 /******************************************************************************/
00157 /*                                 C l o s e                                  */
00158 /******************************************************************************/
00159 
00160 void XrdOucStream::Close(int hold)
00161 {
00162 
00163     // Wait for any associated process on this stream
00164     //
00165     if (!hold) Drain();
00166        else child = 0;
00167 
00168     // Close the associated file descriptor if it was open
00169     //
00170     if (FD >= 0)             close(FD);
00171     if (FE >= 0 && FE != FD) close(FE);
00172 
00173     // Release the buffer if it was allocated.
00174     //
00175     if (buff) free(buff);
00176 
00177     // Clear all data values by attaching a dummy FD
00178     //
00179     FD = FE = -1;
00180     buff = 0;
00181 
00182     // Check if we should echo the last line
00183     //
00184     if (llBuff && Verbose && Eroute)
00185        {if (*llBuff && llBok > 1) Eroute->Say(llPrefix, llBuff);
00186         llBok = 0;
00187        }
00188 }
00189 
00190 /******************************************************************************/
00191 /*                                 D r a i n                                  */
00192 /******************************************************************************/
00193   
00194 int XrdOucStream::Drain() 
00195 {
00196     int Status = 0;
00197 
00198     // Drain any outstanding processes (i.e., kill the process group)
00199     //
00200 #ifndef WIN32
00201     int retc;
00202     if (child) {kill(-child, 9);
00203                 do {retc = waitpid(child, &Status, 0);}
00204                     while(retc > 0 || (retc == -1 && errno == EINTR));
00205                 child = 0;
00206                }
00207 #else
00208     if (child) {
00209        TerminateProcess((HANDLE)child, 0);
00210        child = 0;
00211     }
00212 #endif
00213     return Status;
00214 }
00215   
00216 /******************************************************************************/
00217 /*                                  E c h o                                   */
00218 /******************************************************************************/
00219   
00220 void XrdOucStream::Echo()
00221 {
00222    if (llBok && Verbose && *llBuff && Eroute) Eroute->Say(llPrefix, llBuff);
00223    llBok = 0;
00224 }
00225 
00226 /******************************************************************************/
00227 /*                               E   x   e   c                                */
00228 /******************************************************************************/
00229   
00230 int XrdOucStream::Exec(const char *theCmd, int inrd, int efd)
00231 {
00232     int j;
00233     char *cmd, *origcmd, *parm[MaxARGC];
00234 
00235     // Allocate a buffer for the command as we will be modifying it
00236     //
00237     origcmd = cmd = (char *)malloc(strlen(theCmd)+1);
00238     strcpy(cmd, theCmd);
00239   
00240     // Construct the argv array based on passed command line.
00241     //
00242     for (j = 0; j < MaxARGC-1 && *cmd; j++)
00243         {while(*cmd == ' ') cmd++;
00244          if (!(*cmd)) break;
00245          parm[j] = cmd;
00246          while(*cmd && *cmd != ' ') cmd++;
00247          if (*cmd) {*cmd = '\0'; cmd++;}
00248         }
00249     parm[j] = (char *)0;
00250 
00251     // Continue with normal processing
00252     //
00253     j = Exec(parm, inrd, efd);
00254     free(origcmd);
00255     return j;
00256 }
00257 
00258 int XrdOucStream::Exec(char **parm, int inrd, int efd)
00259 {
00260     int fildes[2], Child_in = -1, Child_out = -1, Child_log = -1;
00261 
00262     // Create a pipe. Minimize file descriptor leaks.
00263     //
00264     if (inrd >= 0)
00265        {if (pipe(fildes))
00266            return Err(Exec, errno, "create input pipe for", parm[0]);
00267            else {
00268                  fcntl(fildes[0], F_SETFD, FD_CLOEXEC);
00269                  Attach(fildes[0]); Child_out = fildes[1];
00270                 }
00271 
00272         if (inrd)
00273            {if (pipe(fildes))
00274                return Err(Exec, errno, "create output pipe for", parm[0]);
00275                else {
00276                      fcntl(fildes[1], F_SETFD, FD_CLOEXEC);
00277                      FE = fildes[1]; Child_in  = fildes[0];
00278                     }
00279            }
00280        } else {Child_out = FD; Child_in = FE;}
00281 
00282     // Handle the standard error file descriptor
00283     //
00284     if (!efd) Child_log = (Eroute ? dup(Eroute->logger()->originalFD()) : -1);
00285        else if (efd > 0) Child_log = efd;
00286 
00287     // Fork a process first so we can pick up the next request. We also
00288     // set the process group in case the chi;d hasn't been able to do so.
00289     //
00290     if ((child = fork()))
00291        {          close(Child_out);
00292         if (inrd) close(Child_in );
00293         if (!efd && Child_log >= 0) close(Child_log);
00294         if (child < 0)
00295            return Err(Exec, errno, "fork request process for", parm[0]);
00296         setpgid(child, child);
00297         return 0;
00298        }
00299 
00300     /*****************************************************************/
00301     /*                  C h i l d   P r o c e s s                    */
00302     /*****************************************************************/
00303 
00304     // Redirect standard in if so requested
00305     //
00306     if (Child_in >= 0)
00307        {if (inrd)
00308            {if (dup2(Child_in, STDIN_FILENO) < 0)
00309                {Erx(Exec, errno, "set up standard in for " <<parm[0]);
00310                 exit(255);
00311                } else if (Child_in != Child_out) close(Child_in);
00312            }
00313        }
00314 
00315     // Reassign the stream to be standard out to capture all of the output.
00316     //
00317     if (Child_out >= 0)
00318        {if (dup2(Child_out, STDOUT_FILENO) < 0)
00319            {Erx(Exec, errno, "set up standard out for " <<parm[0]);
00320             exit(255);
00321            } else close(Child_out);
00322        }
00323 
00324     // Redirect stderr of the stream if we can to avoid keeping the logfile open
00325     //
00326     if (Child_log >= 0)
00327        {if (dup2(Child_log, STDERR_FILENO) < 0)
00328            {Erx(Exec, errno, "set up standard err for " <<parm[0]);
00329             exit(255);
00330            } else close(Child_log);
00331        }
00332 
00333     // Set our process group (the parent should have done this by now) then
00334     // invoke the command never to return
00335     //
00336     setpgid(0,0);
00337     execv(parm[0], parm);
00338     Erx(Exec, errno, "execute " <<parm[0]);
00339     exit(255);
00340 }
00341 
00342 /******************************************************************************/
00343 /*                               G e t L i n e                                */
00344 /******************************************************************************/
00345   
00346 char *XrdOucStream::GetLine()
00347 {
00348    int bcnt, retc;
00349    char *bp;
00350 
00351 // Check if end of message has been reached.
00352 //
00353    if (flags & XrdOucStream_EOM) return (char *)NULL;
00354 
00355 // Find the next record in the buffer
00356 //
00357    if (bleft > 0)
00358       {recp = bnext; bcnt = bleft;
00359        for (bp = bnext; bcnt--; bp++)
00360            if (!*bp || *bp == '\n')
00361                {if (!*bp) flags |= XrdOucStream_EOM;
00362                 *bp = '\0';
00363                 bnext = ++bp;
00364                 bleft = bcnt;
00365                 token = recp;
00366                 return recp;
00367                }
00368                else if (notabs && *bp == '\t') *bp = ' ';
00369   
00370    // There is no next record, so move up data in the buffer.
00371    //
00372       strncpy(buff, bnext, bleft);
00373       bnext = buff + bleft;
00374       }
00375       else bnext = buff;
00376 
00377 // Prepare to read in more data.
00378 //
00379     bcnt = bsize - (bnext - buff) -1;
00380     bp = bnext;
00381 
00382 // Read up to the maximum number of bytes. Stop reading should we see a
00383 // new-line character or a null byte -- the end of a record.
00384 //
00385    recp  = token = buff; // This will always be true at this point
00386    while(bcnt)
00387         {do { retc = read(FD, (void *)bp, (size_t)bcnt); }
00388             while (retc < 0 && errno == EINTR);
00389 
00390          if (retc < 0) {Erp(GetLine,errno,"read request",0); return (char *)0;}
00391          if (!retc)
00392             {*bp = '\0';
00393              flags |= XrdOucStream_EOM;
00394              bnext = ++bp;
00395              bleft = 0;
00396              return buff;
00397             }
00398 
00399          bcnt -= retc;
00400          while(retc--)
00401              if (!*bp || *bp == '\n')
00402                 {if (!*bp) flags |= XrdOucStream_EOM;
00403                     else *bp = '\0';
00404                  bnext = ++bp;
00405                  bleft = retc;
00406                  return buff;
00407                 } else {
00408                  if (notabs && *bp == '\t') *bp = ' ';
00409                  bp++;
00410                 }
00411          }
00412 
00413 // All done, force an end of record.
00414 //
00415    Erp(GetLine, EMSGSIZE, "read full message", 0);
00416    buff[bsize-1] = '\0';
00417    return buff;
00418 }
00419 
00420 /******************************************************************************/
00421 /*                              G e t T o k e n                               */
00422 /******************************************************************************/
00423   
00424 char *XrdOucStream::GetToken(int lowcase) {
00425      char *tpoint;
00426 
00427      // Verify that we have a token to return;
00428      //
00429      if (!token) return (char *)NULL;
00430 
00431      // Skip to the first non-blank character.
00432      //
00433      while (*token && *token == ' ') token ++;
00434      if (!*token) {token = 0; return 0;}
00435      tpoint = token;
00436 
00437      // Find the end of the token.
00438      //
00439      if (lowcase) while (*token && *token != ' ')
00440                         {*token = (char)tolower((int)*token); token++;}
00441         else      while (*token && *token != ' ') {token++;}
00442      if (*token) {*token = '\0'; token++;}
00443 
00444      // All done here.
00445      //
00446      return tpoint;
00447 }
00448 
00449 char *XrdOucStream::GetToken(char **rest, int lowcase)
00450 {
00451      char *tpoint;
00452 
00453      // Get the next token
00454      //
00455      if (!(tpoint = GetToken(lowcase))) return tpoint;
00456 
00457      // Skip to the first non-blank character.
00458      //
00459      while (*token && *token == ' ') token ++;
00460      if (rest) *rest = token;
00461 
00462 
00463      // All done.
00464      //
00465      return tpoint;
00466 }
00467 
00468 /******************************************************************************/
00469 /*                          G e t F i r s t W o r d                           */
00470 /******************************************************************************/
00471 
00472 char *XrdOucStream::GetFirstWord(int lowcase)
00473 {
00474       // If in the middle of a line, flush to the end of the line. Suppress
00475       // variable substitution when doing this to avoid errors.
00476       //
00477       if (xline) 
00478          {XrdOucEnv *oldEnv = SetEnv(0);
00479           while(GetWord(lowcase));
00480           SetEnv(oldEnv);
00481          }
00482       return GetWord(lowcase);
00483 }
00484 
00485 /******************************************************************************/
00486 /*                        G e t M y F i r s t W o r d                         */
00487 /******************************************************************************/
00488   
00489 char *XrdOucStream::GetMyFirstWord(int lowcase)
00490 {
00491    char *var;
00492    int   skip2fi = 0;
00493 
00494 
00495    if (llBok > 1 && Verbose && *llBuff && Eroute) Eroute->Say(llPrefix,llBuff);
00496    llBok = 0;
00497 
00498    if (!myInst)
00499       {if (!myEnv) return add2llB(GetFirstWord(lowcase), 1);
00500           else {while((var = GetFirstWord(lowcase)) && !isSet(var)) {}
00501                 return add2llB(var, 1);
00502                }
00503       }
00504 
00505    do {if (!(var = GetFirstWord(lowcase)))
00506           {if (sawif)
00507               {ecode = EINVAL;
00508                if (Eroute) Eroute->Emsg("Stream", "Missing 'fi' for last 'if'.");
00509               }
00510            return add2llB(var, 1);
00511           }
00512 
00513         if (       !strcmp("if",   var)) var = doif();
00514         if (var && !strcmp("else", var)) var = doelse();
00515         if (var && !strcmp("fi",   var))
00516            {if (sawif) sawif = skpel = skip2fi = 0;
00517                else {if (Eroute)
00518                         Eroute->Emsg("Stream", "No preceeding 'if' for 'fi'.");
00519                      ecode = EINVAL;
00520                     }
00521             continue;
00522            }
00523         if (var && (!myEnv || !isSet(var))) return add2llB(var, 1);
00524        } while (1);
00525 
00526    return 0;
00527 }
00528 
00529 /******************************************************************************/
00530 /*                               G e t W o r d                                */
00531 /******************************************************************************/
00532   
00533 char *XrdOucStream::GetWord(int lowcase)
00534 {
00535      char *wp, *ep;
00536 
00537      // If we have a token, return it
00538      //
00539      xline = 1;
00540      while((wp = GetToken(lowcase)))
00541           {if (!myEnv) return add2llB(wp);
00542            if ((wp = vSubs(wp)) && *wp) return add2llB(wp);
00543           }
00544 
00545      // If no continuation allowed, return a null (but only once)
00546      //
00547      if (!xcont) {xcont = 1; xline = 0; return (char *)0;}
00548 
00549      // Find the next non-blank non-comment line
00550      //
00551      while(GetLine())
00552         {// Get the first token (none if it is a blank line)
00553          //
00554          if (!(wp = GetToken(lowcase))) continue;
00555 
00556          // If token starts with a pound sign, skip the line
00557          //
00558          if (*wp == '#') continue;
00559 
00560          // Process continuations (last non-blank character is a back-slash)
00561          //
00562          ep = bnext-2;
00563          while (ep >= buff && *ep == ' ') ep--;
00564          if (ep < buff) continue;
00565          if (*ep == '\\') {xcont = 1; *ep = '\0';}
00566             else xcont = 0;
00567          return add2llB((myEnv ? vSubs(wp) : wp));
00568          }
00569       xline = 0;
00570       return (char *)0;
00571 }
00572 
00573 /******************************************************************************/
00574 /*                               G e t R e s t                                */
00575 /******************************************************************************/
00576   
00577 int XrdOucStream::GetRest(char *theBuff, int Blen, int lowcase)
00578 {
00579    char *tp, *myBuff = theBuff;
00580    int tlen;
00581 
00582 // Get remaining tokens
00583 //
00584    theBuff[0] = '\0';
00585    while ((tp = GetWord(lowcase)))
00586          {tlen = strlen(tp);
00587           if (tlen+1 >= Blen) return 0;
00588           if (myBuff != theBuff) {*myBuff++ = ' '; Blen--;}
00589           strcpy(myBuff, tp);
00590           Blen -= tlen; myBuff += tlen;
00591          }
00592 
00593 // All done
00594 //
00595    add2llB(0);
00596    return 1;
00597 }
00598 
00599 /******************************************************************************/
00600 /*                              R e t T o k e n                               */
00601 /******************************************************************************/
00602   
00603 void XrdOucStream::RetToken()
00604 {
00605      // Check if we can back up
00606      //
00607      if (!token || token == recp) return;
00608 
00609      // Find the null byte for the token and remove it, if possible
00610      //
00611      while(*token && token != recp) token--;
00612      if (token != recp) 
00613         {if (token+1 != bnext) *token = ' ';
00614          token--;
00615          while(*token && *token != ' ' && token != recp) token--;
00616          if (token != recp) token++;
00617         }
00618 
00619      // If saving line, we must do the same for the saved line
00620      //
00621      if (llBuff)
00622          while(llBcur != llBuff && *llBcur != ' ') {llBcur--; llBleft++;}
00623 }
00624 
00625 /******************************************************************************/
00626 /*                                   P u t                                    */
00627 /******************************************************************************/
00628 
00629 int XrdOucStream::Put(const char *data, const int dlen) {
00630     int dcnt = dlen, retc;
00631 
00632     if (flags & XrdOucStream_BUSY) {ecode = ETXTBSY; return -1;}
00633 
00634     while(dcnt)
00635          {do { retc = write(FE, (const void *)data, (size_t)dlen);}
00636               while (retc < 0 && errno == EINTR);
00637           if (retc >= 0) dcnt -= retc;
00638              else {flags |= XrdOucStream_BUSY;
00639                    Erp(Put, errno, "write to stream", 0);
00640                    flags &= ~XrdOucStream_BUSY;
00641                    return -1;
00642                   }
00643          }
00644     return 0;
00645 }
00646 
00647 int XrdOucStream::Put(const char *datavec[], const int dlenvec[]) {
00648     int i, retc, dlen;
00649     const char *data;
00650 
00651     if (flags & XrdOucStream_BUSY) {ecode = ETXTBSY; return -1;}
00652 
00653     for (i = 0; datavec[i]; i++)
00654         {data = datavec[i]; dlen = dlenvec[i];
00655          while(dlen)
00656               {do { retc = write(FE, (const void *)data, (size_t)dlen);}
00657                    while (retc < 0 && errno == EINTR);
00658                if (retc >= 0) {data += retc; dlen -= retc;}
00659                   else {flags |= XrdOucStream_BUSY;
00660                         Erp(Put, errno, "write to stream",0);
00661                         flags &= ~XrdOucStream_BUSY;
00662                         return -1;
00663                        }
00664               }
00665         }
00666     return 0;
00667 }
00668  
00669 /******************************************************************************/
00670 /*                             W a i t 4 D a t a                              */
00671 /******************************************************************************/
00672 
00673 int XrdOucStream::Wait4Data(int msMax)
00674 {
00675    struct pollfd polltab = {FD, POLLIN|POLLRDNORM, 0};
00676    int retc;
00677 
00678 // Wait until we can actually read something
00679 //
00680    do {retc = poll(&polltab, 1, msMax);} while(retc < 0 && errno == EINTR);
00681    if (retc != 1) return (retc ? errno : -1);
00682 
00683 // Return correct value
00684 //
00685    return (polltab.revents & (POLLIN|POLLRDNORM) ? 0 : EIO);
00686 }
00687   
00688 /******************************************************************************/
00689 /*                       P r i v a t e   M e t h o d s                        */
00690 /******************************************************************************/
00691 /******************************************************************************/
00692 /*                               a d d 2 l l B                                */
00693 /******************************************************************************/
00694 
00695 char *XrdOucStream::add2llB(char *tok, int reset)
00696 {
00697    int tlen;
00698 
00699 // Return if not saving data
00700 //
00701    if (!llBuff) return tok;
00702 
00703 // Check if we should flush the previous line
00704 //
00705    if (reset)
00706       {llBok  = 1;
00707        llBcur = llBuff;
00708        llBleft= llBsz;
00709       *llBuff = '\0';
00710       } else if (!llBok) return tok;
00711                 else {llBok = 2;
00712                       if (llBleft >= 2)
00713                          {*llBcur++ = ' '; *llBcur = '\0'; llBleft--;}
00714                      }
00715 
00716 // Add in the new token
00717 //
00718    if (tok)
00719       {tlen = strlen(tok);
00720        if (tlen < llBsz)
00721           {strcpy(llBcur, tok); llBcur += tlen; llBleft -= tlen;}
00722       }
00723    return tok;
00724 }
00725 /******************************************************************************/
00726 /*                                d o e l s e                                 */
00727 /******************************************************************************/
00728 
00729 char *XrdOucStream::doelse()
00730 {
00731    char *var;
00732 
00733 // An else must be preceeded by an if and not by a naked else
00734 //
00735    if (!sawif || sawif == 2)
00736       {if (Eroute) Eroute->Emsg("Stream", "No preceeding 'if' for 'else'.");
00737        ecode = EINVAL;
00738        return 0;
00739       }
00740 
00741 // If skipping all else caluses, skip all lines until we reach a fi
00742 //
00743    if (skpel)
00744       {while((var = GetFirstWord()))
00745             {if (!strcmp("fi", var)) return var;}
00746        if (Eroute) Eroute->Emsg("Stream", "Missing 'fi' for last 'if'.");
00747        ecode = EINVAL;
00748        return 0;
00749       }
00750 
00751 // Elses are still possible then process one of them
00752 //
00753    do {if (!(var = GetWord())) // A naked else will always succeed
00754           {sawif = 2;
00755            return 0;
00756           }
00757        if (strcmp("if", var))  // An else may only be followed by an if
00758           {Eroute->Emsg("Stream","'else",var,"' is invalid.");
00759            ecode = EINVAL;
00760            return 0;
00761           }
00762        sawif = 0;
00763        var = doif();
00764       } while(var && !strcmp("else", var));
00765    return var;
00766 }
00767   
00768 /******************************************************************************/
00769 /*                                  d o i f                                   */
00770 /******************************************************************************/
00771 
00772 /* Function: doif
00773 
00774    Purpose:  To parse the directive: if [<hlist>] [exec <pgm>] [named <nlist>]
00775                                      fi
00776 
00777             <hlist> Apply subsequent directives until the 'fi' if this host
00778                     is one of the hosts in the blank separated list. Each
00779                     host name may have a single asterisk somewhere in the
00780                     name to indicate where arbitrry characters lie.
00781 
00782             <pgm>   Apply subsequent directives if this program is named <pgm>.
00783 
00784             <nlist> Apply subsequent directives if this  host instance name
00785                     is in the list of blank separated names.
00786 
00787    Notes: 1) At least one of hlist, pgm, or nlist must be specified.
00788           2) The combination of hlist, pgm, nlist must all be true.
00789 
00790    Output: 0 upon success or !0 upon failure.
00791 */
00792 
00793 char *XrdOucStream::doif()
00794 {
00795     char *var;
00796     int rc;
00797 
00798 // Check if the previous if was properly closed
00799 //
00800    if (sawif)
00801       {if (Eroute) Eroute->Emsg("Stream", "Missing 'fi' for last 'if'.");
00802        ecode = EINVAL;
00803       }
00804 
00805 // Check if we should continue
00806 //
00807    sawif = 1; skpel = 0;
00808    if ((rc = XrdOucUtils::doIf(Eroute,*this,"if directive",myHost,myName,myExec)))
00809       {if (rc < 0) ecode = EINVAL;
00810           else skpel = 1;
00811        return 0;
00812       }
00813 
00814 // Skip all lines until we reach a fi or else
00815 //
00816    while((var = GetFirstWord()))
00817         {if (!strcmp("fi",   var)) return var;
00818          if (!strcmp("else", var)) return var;
00819         }
00820 
00821 // Make sure we have a fi
00822 //
00823    if (!var) 
00824       {if (Eroute) Eroute->Emsg("Stream", "Missing 'fi' for last 'if'.");
00825        ecode = EINVAL;
00826       }
00827    return 0;
00828 }
00829 
00830 /******************************************************************************/
00831 /*                                 i s S e t                                  */
00832 /******************************************************************************/
00833   
00834 int XrdOucStream::isSet(char *var)
00835 {
00836    static const char *Mtxt1[2] = {"setenv", "set"};
00837    static const char *Mtxt2[2] = {"Setenv variable", "Set variable"};
00838    static const char *Mtxt3[2] = {"Variable", "Environmental variable"};
00839    char *tp, *vn, *vp, *pv, Vname[64], ec, Nil = 0;
00840    int sawEQ, Set = 1;
00841 
00842 // Process set var = value | set -v | setenv = value
00843 //
00844    if (!strcmp("setenv", var)) Set = 0;
00845       else if (strcmp("set", var)) return 0;
00846 
00847 // Now get the operand
00848 //
00849    if (!(tp = GetToken()))
00850       return xMsg("Missing variable name after '",Mtxt1[Set],"'.");
00851 
00852 // Option flags only apply to set not setenv
00853 //
00854    if (Set)
00855   {if (!strcmp(tp, "-q")) {if (llBuff) {free(llBuff); llBuff = 0;}; return 1;}
00856    if (!strcmp(tp, "-v") || !strcmp(tp, "-V"))
00857       {if (Eroute)
00858           {if (!llBuff) llBuff = (char *)malloc(llBsz);
00859            llBcur = llBuff; llBok = 0; llBleft = llBsz; *llBuff = '\0';
00860            Verbose = (strcmp(tp, "-V") ? 1 : 2);
00861           }
00862        return 1;
00863       }
00864   }
00865 
00866 // Next may be var= | var | var=val
00867 //
00868    if ((vp = index(tp, '='))) {sawEQ = 1; *vp = '\0'; vp++;}
00869       else sawEQ = 0;
00870    if (strlcpy(Vname, tp, sizeof(Vname)) >= sizeof(Vname))
00871       return xMsg(Mtxt2[Set],tp,"is too long.");
00872    if (!Set && !strncmp("XRD", Vname, 3))
00873       return xMsg("Setenv variable",tp,"may not start with 'XRD'.");
00874 
00875 // Verify that variable is only an alphanum
00876 //
00877    tp = Vname;
00878    while (*tp && isalnum(*tp)) tp++;
00879    if (*tp) return xMsg(Mtxt2[Set], Vname, "is non-alphanumeric");
00880 
00881 // Now look for the value
00882 //
00883    if (sawEQ) tp = vp;
00884       else if (!(tp = GetToken()) || *tp != '=')
00885               return xMsg("Missing '=' after", Mtxt1[Set], Vname);
00886               else tp++;
00887    if (!*tp && !(tp = GetToken())) tp = (char *)"";
00888 
00889 // The value may be '$var', in which case we need to get it out of the env if
00890 // this is a set or from our environment if this is a setenv
00891 //
00892    if (*tp != '$') vp = tp;
00893       else {pv = tp+1;
00894                  if (*pv == '(') ec = ')';
00895             else if (*pv == '{') ec = '}';
00896             else if (*pv == '[') ec = ']';
00897             else                 ec = 0;
00898             if (!ec) vn = tp+1;
00899                else {while(*pv && *pv != ec) pv++;
00900                      if (*pv) *pv = '\0';
00901                         else   ec = 0;
00902                      vn = tp+2;
00903                     }
00904             if (!*vn) {*pv = ec; return xMsg("Variable", tp, "is malformed.");}
00905             if (!(vp = (Set ? getenv(vn) : myEnv->Get(vn))))
00906                {if (ec != ']')
00907                    {xMsg(Mtxt3[Set],vn,"is undefined."); *pv = ec; return 1;}
00908                 vp = &Nil;
00909                }
00910             *pv = ec;
00911            }
00912 
00913 // Make sure the value is not too long
00914 //
00915    if ((int)strlen(vp) > maxVLen)
00916       return xMsg(Mtxt3[Set], Vname, "value is too long.");
00917 
00918 // Set the value
00919 //
00920    if (Verbose == 2 && Eroute)
00921       if (!(pv = (Set ? myEnv->Get(Vname) : getenv(Vname))) || strcmp(vp, pv))
00922          {char vbuff[1024];
00923           strcpy(vbuff, Mtxt1[Set]); strcat(vbuff, " "); strcat(vbuff, Vname);
00924           Eroute->Say(vbuff, " = ", vp);
00925          }
00926    if (Set) myEnv->Put(Vname, vp);
00927       else if (!(pv = getenv(Vname)) || strcmp(vp,pv))
00928               XrdOucEnv::Export(Vname, vp);
00929    return 1;
00930 }
00931 
00932 /******************************************************************************/
00933 /*                                 v S u b s                                  */
00934 /******************************************************************************/
00935   
00936 char *XrdOucStream::vSubs(char *Var)
00937 {
00938    char *vp, *sp, *dp, *vnp, ec, bkp, valbuff[maxVLen], Nil = 0;
00939    int n;
00940 
00941 // Check for substitution
00942 //
00943    if (!Var) return Var;
00944    sp = Var; dp = valbuff; n = maxVLen-1; *varVal = '\0';
00945 
00946    while(*sp && n > 0)
00947         {if (*sp == '\\') {*dp++ = *(sp+1); sp +=2; n--; continue;}
00948          if (*sp != '$'
00949          || (!isalnum(*(sp+1)) && !index("({[", *(sp+1))))
00950                 {*dp++ = *sp++;         n--; continue;}
00951          sp++; vnp = sp;
00952               if (*sp == '(') ec = ')';
00953          else if (*sp == '{') ec = '}';
00954          else if (*sp == '[') ec = ']';
00955          else                 ec = 0;
00956          if (ec) {sp++; vnp++;}
00957          while(isalnum(*sp)) sp++;
00958          if (ec && *sp != ec)
00959             {xMsg("Variable", vnp-2, "is malformed."); return varVal;}
00960          bkp = *sp; *sp = '\0';
00961          if (!(vp = myEnv->Get(vnp)))
00962             {if (ec != ']') xMsg("Variable", vnp, "is undefined.");
00963              vp = &Nil;
00964             }
00965          while(n && *vp) {*dp++ = *vp++; n--;}
00966          if (*vp) break;
00967          if (ec) sp++;
00968             else *sp = bkp;
00969         }
00970 
00971    if (*sp) xMsg("Substituted text too long using", Var);
00972       else {*dp = '\0'; strcpy(varVal, valbuff);}
00973    return varVal;
00974 }
00975 
00976 /******************************************************************************/
00977 /*                                  x M s g                                   */
00978 /******************************************************************************/
00979 
00980 int XrdOucStream::xMsg(const char *txt1, const char *txt2, const char *txt3)
00981 {
00982     if (Eroute) Eroute->Emsg("Stream", txt1, txt2, txt3);
00983     ecode = EINVAL;
00984     return 1;
00985 }

Generated on Tue Jul 5 14:46:50 2011 for ROOT_528-00b_version by  doxygen 1.5.1