#ifdef __cplusplus
extern "C" {
#endif
#include <starlet.h>
#include <descrip.h>
#include <uaidef.h>
#include <ssdef.h>
#include <stsdef.h>
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifdef __cplusplus
}
#endif

typedef  struct {short   buflen,          /* Length of output buffer */
                         itmcode;         /* Item code */
                 void    *buffer;         /* Buffer address */
                 void    *retlen;         /* Return length address */
               } ITMLST;

typedef struct {char  *ItemName;         /* Name of the item we're getting */
                unsigned short *ReturnLength; /* Pointer to the return */
                                              /* buffer length */
                void  *ReturnBuffer;     /* generic pointer to the returned */
                                         /* data */
                int   ReturnType;        /* The type of data in the return */
                                         /* buffer */
                int   ItemListEntry;     /* Index of the entry in the item */
                                         /* list we passed to syscall */
              } FetchedItem; /* Use this keep track of the items in the */
                             /* 'grab everything' GETDVI call */ 

#define user_bit_test(a, b, c) \
{ \
    if (c & UIC$M_##b) \
    hv_store(a, #b, strlen(#b), &sv_yes, 0); \
    else \
    hv_store(a, #b, strlen(#b), &sv_no, 0);}   

/* Macro to expand out entries for generic_bitmap_encode */
#define UAI_D(a) { if (!strncmp(FlagName, #a, FlagLen)) { \
                       EncodedValue[0] = EncodedValue[0] | DMT$M_##a; \
                       break; \
                     } \
                 }

/* Macro to define an entry in the user info array */
#define UAI_ENT(a, b, c) {#a, UAI$_##a, b, c}

#define IS_STRING 1
#define IS_LONGWORD 2
#define IS_QUADWORD 3
#define IS_WORD 4
#define IS_BYTE 5
#define IS_VMSDATE 6
#define IS_BITMAP 7   /* Each bit in the return value indicates something */
#define IS_ENUM 8     /* Each returned value has a name, and we ought to */
                      /* return the name instead of the value */
#define IS_ODD 9      /* A catchall */
#define IS_COUNTSTRING 10 /* A counted string--first byte's the count */

struct GenericID {
  char *ItemName; /* Pointer to the item name */
  int  SyscallValue;      /* Value to use in the getDVI item list */
  int  BufferLen;     /* Length the return va buf needs to be. (no nul */
                      /* terminators, so must be careful with the return */
                      /* values. */
  int  ReturnType;    /* Type of data the item returns */
};


struct GenericID UserInfoList[] = {
  UAI_ENT(ACCOUNT, 32, IS_STRING),
  UAI_ENT(ASTLM, 2, IS_WORD),
  UAI_ENT(BATCH_ACCESS_P, 3, IS_ODD),
  UAI_ENT(BATCH_ACCESS_S, 3, IS_ODD),
  UAI_ENT(BIOLM, 2, IS_WORD),
  UAI_ENT(BYTLM, 4, IS_LONGWORD),
  UAI_ENT(CLITABLES, 32, IS_COUNTSTRING),
  UAI_ENT(CPUTIM, 4, IS_LONGWORD),
  UAI_ENT(DEFCLI, 32, IS_COUNTSTRING),
  UAI_ENT(DEFDEV, 32, IS_COUNTSTRING),
  UAI_ENT(DEFDIR, 64, IS_COUNTSTRING),
  UAI_ENT(DEF_PRIV, 8, IS_QUADWORD),
  UAI_ENT(DFWSCNT, 4, IS_LONGWORD),
  UAI_ENT(DIOLM, 2, IS_WORD),
  UAI_ENT(DIALUP_ACCESS_P, 3, IS_ODD),
  UAI_ENT(DIALUP_ACCESS_S, 3, IS_ODD),
  UAI_ENT(ENCRYPT, 1, IS_ENUM),
  UAI_ENT(ENCRYPT2, 1, IS_ENUM),
  UAI_ENT(ENQLM, 2, IS_WORD),
  UAI_ENT(EXPIRATION, 8, IS_VMSDATE),
  UAI_ENT(FILLM, 2, IS_WORD),
  UAI_ENT(FLAGS, 4, IS_BITMAP),
  UAI_ENT(JTQUOTA, 4, IS_LONGWORD),
  UAI_ENT(LASTLOGIN_I, 8, IS_VMSDATE),
  UAI_ENT(LASTLOGIN_N, 8, IS_VMSDATE),
  UAI_ENT(LGICMD, 64, IS_COUNTSTRING),
  UAI_ENT(LOCAL_ACCESS_P, 3, IS_ODD),
  UAI_ENT(LOCAL_ACCESS_S, 3, IS_ODD),
  UAI_ENT(LOGFAILS, 2, IS_WORD),
  UAI_ENT(MAXACCTJOBS, 2, IS_WORD),
  UAI_ENT(MAXDETACH, 2, IS_WORD),
  UAI_ENT(MAXJOBS, 2, IS_WORD),
  UAI_ENT(NETWORK_ACCESS_P, 3, IS_ODD),
  UAI_ENT(NETWORK_ACCESS_S, 3, IS_ODD),
  UAI_ENT(OWNER, 32, IS_COUNTSTRING),
  UAI_ENT(PBYTLM, 4, IS_LONGWORD),
  UAI_ENT(PGFLQUOTA, 4, IS_LONGWORD),
  UAI_ENT(PRCCNT, 4, IS_LONGWORD),
  UAI_ENT(PRI, 1, IS_BYTE),
  UAI_ENT(PRIV, 8, IS_QUADWORD),
  UAI_ENT(PWD, 8, IS_STRING),
  UAI_ENT(PWD_DATE, 8, IS_VMSDATE),
  UAI_ENT(PWD_LENGTH, 4, IS_LONGWORD),
  UAI_ENT(PWD_LIFETIME, 8, IS_VMSDATE),
  UAI_ENT(PWD2, 8, IS_QUADWORD),
  UAI_ENT(PWD2_DATE, 8, IS_VMSDATE),
  UAI_ENT(QUEPRI, 1, IS_BYTE),
  UAI_ENT(REMOTE_ACCESS_P, 3, IS_ODD),
  UAI_ENT(REMOTE_ACCESS_S, 3, IS_ODD),
  UAI_ENT(SALT, 2, IS_WORD),
  UAI_ENT(SHRFILLM, 2, IS_WORD),
  UAI_ENT(TQCNT, 2, IS_WORD),
  UAI_ENT(UIC, 4, IS_LONGWORD),
  UAI_ENT(USER_DATA, 255, IS_STRING),
  UAI_ENT(WSEXTENT, 4, IS_LONGWORD),
  UAI_ENT(WSQUOTA, 4, IS_LONGWORD),
  {NULL, 0, 0, 0}
};

char *MonthNames[12] = {
  "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
  "Oct", "Nov", "Dec"} ;

/* Globals to track how many different pieces of info we can return, as */
/* well as how much space we'd need to grab to store it. */
static int UserInfoCount = 0;
static int UserInfoMallocSize = 0;

/* Macro to fill in a 'traditional' item-list entry */
#define init_itemlist(ile, length, code, bufaddr, retlen_addr) \
{ \
    (ile)->buflen = (length); \
    (ile)->itmcode = (code); \
    (ile)->buffer = (bufaddr); \
    (ile)->retlen = (retlen_addr) ;}

/* Take a pointer to a bitmap hash (like decode_bitmap gives), an item */
/* code, and a pointer to the output buffer and encode the bitmap */
void
generic_bitmap_encode(HV * FlagHV, int ItemCode, void *Buffer)
{
  char *FlagName;
  I32 FlagLen;
  int *EncodedValue; /* Pointer to an integer

  /* Shut Dec C up */
  FlagName = NULL;

  /* Buffer's a pointer to an integer array, really it is */
  EncodedValue = Buffer;

  /* Initialize our hash iterator */
  hv_iterinit(FlagHV);

  /* Rip through the hash */
  while (hv_iternextsv(FlagHV, &FlagName, &FlagLen)) {
    
    switch (ItemCode) {
    default:
      croak("Invalid item specified");
    }
  }
}

/* Take a pointer to an itemlist, a hashref, and some flags, and build up */
/* an itemlist from what's in the hashref. Buffer space for the items is */
/* allocated, as are the length shorts and stuff. If the hash entries have */
/* values, those values are copied into the buffers, too. Returns the */
/* number of items stuck in the itemlist */
int build_itemlist(struct GenericID InfoList[], ITMLST *ItemList, HV *HashRef)
{
  /* standard, dopey index variable */
  int i = 0;
  int ItemListIndex = 0;
  char *TempCharPointer;
  unsigned int TempStrLen;
  
  int TempNameLen;
  SV *TempSV;
  unsigned short *TempLen;
  int ItemCode;
  char *TempBuffer;
  char StringLength;
  int BufferLength;
  long TempLong;
  struct dsc$descriptor_s TimeStringDesc;
  int Status;
  int CopyData;

  for(i = 0; InfoList[i].ItemName; i++) {
    TempNameLen = strlen(InfoList[i].ItemName);
    /* Figure out some stuff. Avoids duplication, and makes the macro */
    /* expansion of init_itemlist a little easier */
    ItemCode = InfoList[i].SyscallValue;
    CopyData = TRUE;
    switch(InfoList[i].ReturnType) {
      /* Quadwords are treated as strings for right now */
    case IS_QUADWORD:
    case IS_COUNTSTRING:
    case IS_STRING:
      /* Allocate us some buffer space */
      Newz(NULL, TempBuffer, InfoList[i].BufferLen, char);
      Newz(NULL, TempLen, 1, unsigned short);
      
      BufferLength = InfoList[i].BufferLen;
      
      /* Set the string buffer to spaces */
      memset(TempBuffer, ' ', InfoList[i].BufferLen);
        
      /* If we're copying data, then fetch it and stick it in the buffer */
      if (CopyData) {
        TempSV = *hv_fetch(HashRef,
                           InfoList[i].ItemName,
                           TempNameLen, FALSE);
        TempCharPointer = SvPV(TempSV, TempStrLen);
        
        /* If there was something in the SV, then copy it over */
        if (TempStrLen) {
          BufferLength = TempStrLen < InfoList[i].BufferLen
            ? TempStrLen : InfoList[i].BufferLen;
          Copy(TempCharPointer, TempBuffer, BufferLength, char);
        }
      }
      
      init_itemlist(&ItemList[ItemListIndex],
                    BufferLength,
                    ItemCode,
                    TempBuffer,
                    TempLen);
      break;
    case IS_VMSDATE:
      /* Allocate us some buffer space */
      Newz(NULL, TempBuffer, InfoList[i].BufferLen, char);
      Newz(NULL, TempLen, 1, unsigned short);
      
      if (CopyData) {
        TempSV = *hv_fetch(HashRef,
                           InfoList[i].ItemName,
                           TempNameLen, FALSE);
        TempCharPointer = SvPV(TempSV, TempStrLen);
        
        /* Fill in the time string descriptor */
        TimeStringDesc.dsc$a_pointer = TempCharPointer;
        TimeStringDesc.dsc$w_length = TempStrLen;
        TimeStringDesc.dsc$b_dtype = DSC$K_DTYPE_T;
        TimeStringDesc.dsc$b_class = DSC$K_CLASS_S;
        
        /* Convert from an ascii rep to a VMS quadword date structure */
        Status = sys$bintim(&TimeStringDesc, TempBuffer);
        if (Status != SS$_NORMAL) {
          croak("Error converting time!");
        }
      }
      
      init_itemlist(&ItemList[ItemListIndex],
                    InfoList[i].BufferLen,
                    ItemCode,
                    TempBuffer,
                    TempLen);
      break;
      
    case IS_LONGWORD:
      /* Allocate us some buffer space */
      Newz(NULL, TempBuffer, InfoList[i].BufferLen, char);
      Newz(NULL, TempLen, 1, unsigned short);
      
      if (CopyData) {
        TempSV = *hv_fetch(HashRef,
                           InfoList[i].ItemName,
                           TempNameLen, FALSE);
        TempLong = SvIVX(TempSV);
        
        /* Set the value */
        *TempBuffer = TempLong;
      }
      
      init_itemlist(&ItemList[ItemListIndex],
                    InfoList[i].BufferLen,
                    ItemCode,
                    TempBuffer,
                    TempLen);
      break;
      
    case IS_BITMAP:
      /* Allocate us some buffer space */
      Newz(NULL, TempBuffer, InfoList[i].BufferLen, char);
      Newz(NULL, TempLen, 1, unsigned short);
      
      if (CopyData) {
        TempSV = *hv_fetch(HashRef,
                           InfoList[i].ItemName,
                           TempNameLen, FALSE);
        
        /* Is the SV an integer? If so, then we'll use that value. */
        /* Otherwise we'll assume that it's a hashref of the sort that */
        /* generic_bitmap_decode gives */
        if (SvIOK(TempSV)) {
          TempLong = SvIVX(TempSV);
          /* Set the value */
          *TempBuffer = TempLong;
        } else {
          generic_bitmap_encode((HV *)SvRV(TempSV), ItemCode, TempBuffer);
        }
      }
      
      init_itemlist(&ItemList[ItemListIndex],
                    InfoList[i].BufferLen,
                    ItemCode,
                    TempBuffer,
                    TempLen);
      break;
      
    default:
      croak("Unknown item type found!");
      break;
    }
    ItemListIndex++;
  }

  return(ItemListIndex);
}

/* Takes an item list pointer and a count of items, and frees the buffer */
/* memory and length buffer memory */
void
tear_down_itemlist(ITMLST *ItemList, int NumItems)
{
  int i;

  for(i=0; i < NumItems; i++) {
    if(ItemList[i].buffer != NULL)
      Safefree(ItemList[i].buffer);
    if(ItemList[i].retlen != NULL)
      Safefree(ItemList[i].retlen);
  }
}
         
char *
de_enum(int UAICode, int UAIVal)
{
  switch(UAICode) {
  case UAI$_ENCRYPT:
    switch (UAIVal) {
    case UAI$C_AD_II:
      return "AD_II";
    case UAI$C_PURDY:
      return "PURDY";
    case UAI$C_PURDY_V:
      return "PURDY_V";
    case UAI$C_PURDY_S:
      return "PURDY_S";
    default:
      return "Unknown";
    }
  case UAI$_ENCRYPT2:
    switch (UAIVal) {
    case UAI$C_AD_II:
      return "AD_II";
    case UAI$C_PURDY:
      return "PURDY";
    case UAI$C_PURDY_V:
      return "PURDY_V";
    case UAI$C_PURDY_S:
      return "PURDY_S";
    default:
      return "Unknown";
    }
  default:
    return "Unknown";
  }
}

void
tote_up_items()
{
  for(UserInfoCount = 0; UserInfoList[UserInfoCount].ItemName;
      UserInfoCount++) {
    /* While we're here, we might as well get a generous estimate of how */
    /* much space we'll need for all the buffers */
    UserInfoMallocSize += UserInfoList[UserInfoCount].BufferLen;
    /* Add in a couple extra, just to be safe */
    UserInfoMallocSize += 8;
  }
}    

/* This routine takes a DVI item list ID and the value that wants to be */
/* de-enumerated and returns a pointer to an SV with the de-enumerated name */
/* in it */
SV *
enum_name(long UAICode, long UAIVal)
{
  SV *WorkingSV = newSV(10);
  char TempSprintfBuffer[512];
  switch(UAICode) {
  case UAI$_ENCRYPT:
  case UAI$_ENCRYPT2:
    switch (UAIVal) {
    case UAI$C_AD_II:
      sv_setpv(WorkingSV,  "AD_II");
      break;
    case UAI$C_PURDY:
      sv_setpv(WorkingSV,  "PURDY");
      break;
    case UAI$C_PURDY_V:
      sv_setpv(WorkingSV,  "PURDY_V");
      break;
    case UAI$C_PURDY_S:
      sv_setpv(WorkingSV,  "PURDY_S");
      break;
    default:
      sprintf(TempSprintfBuffer, "Unknown encrypt type (%i)",UAIVal); 
      sv_setpv(WorkingSV,  TempSprintfBuffer);
      break;
    }
    break;
  default:
    sv_setpv(WorkingSV,  "Unknown");
  }
  
  return WorkingSV;
}

/* This routine gets passed a pre-cleared array that's big enough for all */
/* the pieces we'll fill in, and that has the input parameter stuck in */
/* entry 0. We allocate the memory and fill in the rest of the array, and */
/* pass back a hash that has all the return results in it. */
SV *
generic_getuai_call(ITMLST *ListOItems, int InfoCount, SV *UserName)

{
  FetchedItem *OurDataList;
  unsigned short *ReturnLengths;
  int i, LocalIndex;
  unsigned int PVLength;
  int status;
  HV *AllPurposeHV;
  SV *ReturnedSV;
  unsigned short ReturnedTime[7];
  char AsciiTime[100];
  char QuadWordString[65];
  char *TempCharPointer;
  short *TempWordPointer;
  long *TempLongPointer;
  __int64 *TempQuadPointer;
  char *TempStringBuffer;
  char StringLength;
  long EnumVal;
  struct dsc$descriptor_s UserDesc;

  UserDesc.dsc$a_pointer = SvPV(UserName, PVLength);
  UserDesc.dsc$w_length = PVLength;
  UserDesc.dsc$b_dtype = DSC$K_DTYPE_T;
  UserDesc.dsc$b_class = DSC$K_CLASS_S;
  
  LocalIndex = 0;
  
  /* Allocate the local tracking array */
  OurDataList = malloc(sizeof(FetchedItem) * InfoCount);
  memset(OurDataList, 0, sizeof(FetchedItem) * InfoCount);
  
  /* We also need room for the buffer lengths */
  ReturnLengths = malloc(sizeof(short) * InfoCount);
  memset(ReturnLengths, 0, sizeof(short) * InfoCount);
  
  
  /* Fill in the item list and the tracking list */
  for (i = 0; UserInfoList[i].ItemName; i++) {
    /* Allocate the return data buffer and zero it. Can be oddly
       sized, so we use the system malloc instead of New */
    OurDataList[LocalIndex].ReturnBuffer =
      malloc(UserInfoList[i].BufferLen);
    memset(OurDataList[LocalIndex].ReturnBuffer, 0,
           UserInfoList[i].BufferLen); 
    
    /* Note some important stuff (like what we're doing) in our local */
    /* tracking array */
    OurDataList[LocalIndex].ItemName =
      UserInfoList[i].ItemName;
    OurDataList[LocalIndex].ReturnLength =
      &ReturnLengths[LocalIndex];
    OurDataList[LocalIndex].ReturnType =
      UserInfoList[i].ReturnType;
    OurDataList[LocalIndex].ItemListEntry = i;
    
    /* Fill in the item list */
    init_itemlist(&ListOItems[LocalIndex], UserInfoList[i].BufferLen,
                  UserInfoList[i].SyscallValue,
                  OurDataList[LocalIndex].ReturnBuffer,
                  &ReturnLengths[LocalIndex]);
    
    /* Increment the local index */
    LocalIndex++;
  }

  /* Make the GETQUIW call */
  status = sys$getuai(0, NULL, &UserDesc, ListOItems, NULL, NULL, NULL);

  /* Did it go OK? */
  if (status == SS$_NORMAL) {
    /* Looks like it */
    AllPurposeHV = newHV();
    for (i = 0; i < LocalIndex; i++) {
      switch(OurDataList[i].ReturnType) {
      case IS_ODD:
      case IS_STRING:
        /* copy the return string into a temporary buffer with C's string */
        /* handling routines. For some reason $GETQUI returns values with */
        /* embedded nulls and bogus lengths, which is really */
        /* strange. Anyway, this is a cheap way to see how long the */
        /* string is without doing a strlen(), which might fall off the */
        /* end of the world */
        TempStringBuffer = malloc(*(OurDataList[i].ReturnLength) + 1);
        memset(TempStringBuffer, 0, *(OurDataList[i].ReturnLength) + 1);
        strncpy(TempStringBuffer, OurDataList[i].ReturnBuffer,
                *(OurDataList[i].ReturnLength));
        if (strlen(TempStringBuffer) < *OurDataList[i].ReturnLength)
          *OurDataList[i].ReturnLength = strlen(TempStringBuffer);
        free(TempStringBuffer);
        /* Check to make sure we got something back, otherwise set the */
        /* value to undef */
        if (*OurDataList[i].ReturnLength) {
          hv_store(AllPurposeHV, OurDataList[i].ItemName,
                   strlen(OurDataList[i].ItemName),
                   newSVpv(OurDataList[i].ReturnBuffer,
                           *(OurDataList[i].ReturnLength)), 0);
        } else {
          hv_store(AllPurposeHV, OurDataList[i].ItemName,
                   strlen(OurDataList[i].ItemName),
                   &PL_sv_undef, 0);
        }
        break;
      case IS_COUNTSTRING:
        /* Check to make sure we got something back, otherwise set the */
        /* value to undef */        
        if (*OurDataList[i].ReturnLength) {
          StringLength = *(char *)OurDataList[i].ReturnBuffer;
          hv_store(AllPurposeHV, OurDataList[i].ItemName,
                   strlen(OurDataList[i].ItemName),
                   newSVpv((char *)OurDataList[i].ReturnBuffer + 1,
                           StringLength), 0);
        } else {
          hv_store(AllPurposeHV, OurDataList[i].ItemName,
                   strlen(OurDataList[i].ItemName),
                   &PL_sv_undef, 0);
        }
        break;
      case IS_VMSDATE:
        sys$numtim(ReturnedTime, OurDataList[i].ReturnBuffer);
        sprintf(AsciiTime, "%02hi-%s-%hi %02hi:%02hi:%02hi.%hi",
                ReturnedTime[2], MonthNames[ReturnedTime[1] - 1],
                ReturnedTime[0], ReturnedTime[3], ReturnedTime[4],
                ReturnedTime[5], ReturnedTime[6]);
        hv_store(AllPurposeHV, OurDataList[i].ItemName,
                 strlen(OurDataList[i].ItemName),
                 newSVpv(AsciiTime, 0), 0);
        break;
      case IS_ENUM:
        if ( 1 == *OurDataList[i].ReturnLength) {
          EnumVal = *(char *)OurDataList[i].ReturnBuffer;
        } else {
           if ( 4 == *OurDataList[i].ReturnLength) {
             EnumVal = *(short *)OurDataList[i].ReturnBuffer;
           } else {
             if ( 4 == *OurDataList[i].ReturnLength) {
               EnumVal = *(long *)OurDataList[i].ReturnBuffer;
             }
           }
         }
        hv_store(AllPurposeHV, OurDataList[i].ItemName,
                 strlen(OurDataList[i].ItemName),
                 enum_name(UserInfoList[i].SyscallValue,
                           EnumVal), 0);
        break;
      case IS_BITMAP:
      case IS_LONGWORD:
        TempLongPointer = OurDataList[i].ReturnBuffer;
        hv_store(AllPurposeHV, OurDataList[i].ItemName,
                 strlen(OurDataList[i].ItemName),
                 newSViv(*TempLongPointer),
                 0);
        break;
      case IS_WORD:
        TempWordPointer = OurDataList[i].ReturnBuffer;
        hv_store(AllPurposeHV, OurDataList[i].ItemName,
                 strlen(OurDataList[i].ItemName),
                 newSViv(*TempWordPointer),
                 0);
        break;
      case IS_BYTE:
        TempCharPointer = OurDataList[i].ReturnBuffer;
        hv_store(AllPurposeHV, OurDataList[i].ItemName,
                 strlen(OurDataList[i].ItemName),
                 newSViv(*TempCharPointer),
                 0);
        break;
      case IS_QUADWORD:
        TempQuadPointer = OurDataList[i].ReturnBuffer;
        sprintf(QuadWordString, "%llu", *TempQuadPointer);
        hv_store(AllPurposeHV, OurDataList[i].ItemName,
                 strlen(OurDataList[i].ItemName),
                 newSVpv(QuadWordString, 0), 0);
        break;
        
      }
    }
    /* Set the returned status and return the HV we built */
    ReturnedSV = newRV_noinc((SV *) AllPurposeHV);
  } else {
    /* Well, things weren't fine and dandy. */
    if (SS$_BADPARAM == status)
      printf("Badparam\n");
    if (RMS$_RSZ == status)
      printf("RSZ\n");
    if (SS$_ACCVIO == status)
      printf("ACCVIO\n");
    SETERRNO(EVMSERR, status);
    ReturnedSV = &PL_sv_undef;
  }

  
  /* Free up our allocated memory */
  for(i = 0; i < InfoCount; i++) {
    free(OurDataList[i].ReturnBuffer);
  }
  free(OurDataList);
  free(ReturnLengths);

  return(ReturnedSV);
}


MODULE = VMS::User		PACKAGE = VMS::User		

void
user_info(UserName)
     SV *UserName
   PPCODE:
{
  
  ITMLST *ListOItems;
  unsigned short ReturnBufferLength = 0;
  unsigned int UserFlags;
  unsigned short UserFlagsLength;
  unsigned int Status;
  unsigned int ReturnedUAIStatus;
  unsigned int SubType;
  
  /* If we've not gotten the count of items, go get it now */
  if (UserInfoCount == 0) {
    tote_up_items();
  }

  /* We need room for our item list */
  ListOItems = malloc(sizeof(ITMLST) * (UserInfoCount + 1));
  memset(ListOItems, 0, sizeof(ITMLST) * (UserInfoCount + 1));

  /* Make the call to the generic fetcher and make it the return */
  /* value. We don't need to go messing with the item list, since what we */
  /* used for the last call is OK to pass along to this one. */
  XPUSHs(generic_getuai_call(ListOItems, UserInfoCount, UserName));
  /* Give back the allocated item list memory */
  free(ListOItems);
}