[comp.os.vms] SUMMARY - How to find the next available uic

ARJAN@HROEUR51.BITNET (06/30/88)

My posting about scanning the rightsdatabase for finding
unused uic's in a given group has ripened for exacly two
months (posting of 29-apr-1988) before I received it back
myself via infovax, though some people must have received
it earlier (some reactions a few days after the posting),
but with the original posting yesterday (29-jun-1988) I
received another amount of reactions.
For all those people who sent me the key to the answer:
Thanks a lot.
For all those interested I'll give a short recipe plus the
code I knocked together.


First, one has to set up the FAB/RAB/XABKEY structures and
have them point to each other.
Since you'll be reading, the various fields in those structu-
res need not (all) be defined -RMS will handle that for you.
Open the file, start record stream, find the group uic
([xxx,177777]), using a sys$find (record access mode = KEYED).
(This is a check to be sure that the group exists.)
Then you sys$find [xxx,0] with record processing option
KEY GREATER THAN OR EQUAL TO. From there you sys$get the
next record with record access mode SEQUENTIAL i.o KEYED
and record processing option KEY GREATER THAN.
whenever you find [xxx,177777] again, you know that the
previous record was the record of the last uic in that group.

Follows the program.
(I know it's not not that elegant, but at least it
does what I want it to do, and it does so real fast)
-No privs needed, because rightslist.dat is protected w:re

------------------------------------------------------------------

/* FIND_UIC.C -- Find first free uic for a given group. */
/* and set symbol FULL_UIC to be this new uic.          */

# include rms         /* = fab + rab + nam + xab + rmsdef */
# include ssdef
# include stdio
# include descrip

int int_group, int_rec_mem_val, n, status, suc(), fail();
short int group[2], rec_grp_val[2];
int swtch;             /* for switching record buffers */

struct FAB rdbfab;    /* File access block */
struct XABKEY rdbxab_key_1; /* Extended attributes block */
struct RAB rdbrab;    /* Record access block */
static char filename[] = { "SYS$SYSTEM:RIGHTSLIST.DAT" };
char record[2][65];      /* The record(s) to find */

main()
{

rdbfab = cc$rms_fab;                    /* Initialize structures */
rdbrab = cc$rms_rab;
rdbxab_key_1 = cc$rms_xabkey;

rdbfab.fab$l_xab = &rdbxab_key_1;       /* Define fab's xab (key) pointer */
rdbrab.rab$l_fab = &rdbfab;             /* Rab's fab pointer */

rdbfab.fab$b_fac = FAB$M_GET;           /* File access: read only , share */
rdbfab.fab$b_shr = FAB$M_SHRGET;
rdbxab_key_1.xab$b_dtp = XAB$C_BN4;     /* Unsigned 4 byte binary ascending */
rdbfab.fab$l_fna = filename;            /* predefined file name */
rdbfab.fab$b_fns = strlen(filename);


printf("UIC group: ");
scanf("%o",&group[1]);                  /* Inquire for octal uic group */

int_group = group[1];
group[0] = -1;                          /* Group identifier */

status = sys$open(&rdbfab);             /* Opens for (shared) read only */
if(status!=RMS$_NORMAL) lib$stop(status);

rdbrab.rab$b_mbf = 5; /* Multibuffer count (?) (Minim. 2 for indexed files) */

status = sys$connect(&rdbrab);          /* Start record stream */
if(status!=RMS$_NORMAL) lib$stop(status);

find_highest_mem(group,record[swtch=0]);
}

find_highest_mem(gr,rc)
int *gr;
char *rc;
{

rdbrab.rab$b_krf = 0;  /* Key of referece; - Identifier field (Okay, default) */
rdbrab.rab$l_kbf = gr; /* Key value of the key of reference - (Identifier) */
rdbrab.rab$b_ksz = 4;
rdbrab.rab$b_rac = RAB$C_KEY; /* record access mode: random access by key */
rdbrab.rab$l_ubf = rc; /* record buffer address */
rdbrab.rab$w_usz = 64; /* record length */

if(int_group!=1) /* No group identifier for [1,*] */
  {
   sys$clref(1);
   status = sys$find(&rdbrab,&fail,&suc);
   if(status!=RMS$_NORMAL && status!=RMS$_PENDING)
     {
      puts("%FIND_NEW_UIC-F-NOGRP, specified group does not exist");
      exit();
     }
   sys$waitfr(1);
  }

group[0] = 0;                 /* Group exists */
rdbrab.rab$l_rop = RAB$M_KGE; /* Rec. proc. opt: key gr than or eq. to */

sys$clref(1);

status = sys$find(&rdbrab,&fail,&suc);
if(status!=RMS$_NORMAL && status!=RMS$_PENDING)
  {
   puts("%FIND_NEW_UIC-W-UNKNWN, unknown error");
   exit();
  }

sys$waitfr(1);

rdbrab.rab$l_ubf = record[swtch=1]; /* second record buffer address */

while(1)
 {
  sys$clref(1);
  rdbrab.rab$b_rac = RAB$C_SEQ; /* Found first record, now read seqential. */
  rdbrab.rab$l_rop = RAB$M_KGT; /* Rec. processing option: key greater than */
  rdbrab.rab$l_ubf = record[swtch]; /* one of the  two record buff addresses */

  status = sys$get(&rdbrab,&fail,&suc);
  if(status!=RMS$_NORMAL && status!=RMS$_PENDING) lib$stop(status);
  sys$waitfr(1);
 }

status = sys$close(&rdbfab);
if(status!=RMS$_NORMAL) lib$stop(status);
}

suc() /* RMS completed (successfully) -- AST success routine */
{
 int n;
 rec_grp_val[0] = record[swtch][0] + record[swtch][1]*256;
 rec_grp_val[1] = record[swtch][2] + record[swtch][3]*256;
 if(rec_grp_val[0]==-257)
   {
    if(swtch==0) swtch=1; else swtch=0;
    int_rec_mem_val = record[swtch][0] + record[swtch][1]*256 + 1;
    set_uic_symbol();
    exit();
   }
 if(swtch==0) swtch=1; else swtch=0;
 sys$setef(1);
}

fail() /* RMS completed (unsuccessfully) -- AST error routine */
  {
   puts("%FIND_NEW_UIC-F-NOGRP, specified group does not exist");
   exit();
  }

set_uic_symbol()
{
int global=2;
char newuic[16], memnum[7], grpnum[7];
struct  dsc$descriptor_s uic_desc, symbol_desc;
static char brace[]={ "]" }, comma[]={ "," }, uic_symbol[]={ "FULL_UIC" };

 otoa(int_group,&grpnum);
 otoa(int_rec_mem_val,&memnum);
 newuic[0]='['; newuic[1]='\0';
 strcat(newuic,grpnum);
 strcat(newuic,comma);
 strcat(newuic,memnum);
 strcat(newuic,brace);

 uic_desc.dsc$w_length=strlen(uic_symbol);     /* Symbol name length */
 uic_desc.dsc$b_dtype=DSC$K_DTYPE_T;
 uic_desc.dsc$b_class=DSC$K_CLASS_S;
 uic_desc.dsc$a_pointer=uic_symbol;

 symbol_desc.dsc$w_length=strlen(newuic);           /* Symbol length */
 symbol_desc.dsc$b_dtype=DSC$K_DTYPE_T;
 symbol_desc.dsc$b_class=DSC$K_CLASS_S;
 symbol_desc.dsc$a_pointer=newuic;

 status=lib$set_symbol(&uic_desc,&symbol_desc,&global); /* set global symbol */
 if (status!=SS$_NORMAL) lib$stop(status);
}

carl@CITHEX.CALTECH.EDU (Carl J Lydick) (07/05/88)

> Whenever we want to add new users to sysuaf.dat, we try to find
> the next uic available for this new user. (For accounting reasons
> we do NOT use any uic that has been used before). The only way I
> could think of was to scan sysuaf.dat completely, using $IDTOASC
> in a wildcard operation, translate the usernames back to individual
> UIC's with $ASCTOID, then examining the group and member value, thus
> finding the highest member value of the specified group.
> But this takes about THIRTY times as much cpu time as does AUTHORIZE
> with a SHOW [XXXX,*]/BRIEF
>
> Does anybody know which calls authorize might be using ?

Since there's been one response to the net describing how to do this in C, and
one  in  MACRO,  I  just thought I'd show how it's done in FORTRAN, viz., much
more easily than in either of the other two languages.  I tried  doing  it  in
DCL,  which  would  have  been as simple as the FORTRAN code, but the DCL read
statement doesn't like the key value to have null bytes in  it  (It  sees  the
null  and  decides  that's  the  end  of the command line!  Is this a bug or a
feature?).  At any rate, here's the FORTRAN program:
********************************************************************************
	PROGRAM NEXT_FREE_UIC
	CHARACTER*6 MEMBER
	INTEGER GROUP, KEYNUM, UIC, NEXT_UIC
	OPEN(UNIT=1,FILE='SYS$SYSTEM:RIGHTSLIST.DAT',SHARED,READONLY,
	1	ACCESS='KEYED',STATUS='OLD',FORM='UNFORMATTED')
	TYPE 10
10	FORMAT(' GROUP: ',$)
	READ 20, GROUP
20	FORMAT(O6)
	KEYNUM = GROUP * '10000'X
	UIC = KEYNUM
	READ(1,KEYGE=KEYNUM,KEYID=0,ERR=900) NEXT_UIC
30	IF ((NEXT_UIC/'10000'X) .NE. GROUP) GOTO 900
	IF (IAND(NEXT_UIC,'FFFF'X) .EQ. 'FFFF'X) GOTO 900
	UIC = NEXT_UIC
	READ(1,ERR=900) NEXT_UIC
	GOTO 30
900	UIC = UIC + 1
	IF (IAND(UIC,'FFFF'X) .EQ. 'FFFF'X) GOTO 950
	TYPE 910, UIC/'10000'X, IAND(UIC,'FFFF'X)
910	FORMAT(1X,'THE NEXT UIC IN THE GROUP IS [',O6.6,',',O6.6,']')
	WRITE(MEMBER,920) IAND(UIC,'FFFF'X)
920	FORMAT(O6.6)
	CALL LIB$SET_SYMBOL('MEMBER', MEMBER)
	GOTO 999
950	TYPE 960, GROUP
960	FORMAT(' GROUP', O6.6, ' IS FULL')
999	END
********************************************************************************
If there were a KEYLT specifier, the loop could be eliminated, and it could
all be done with a single read from the file, by setting KEYNUM equal to
GROUP * '10000'X + 'FFFF'X.