From:	AITGW::"goathunter%WKUVX1.BITNET@VM1.NoDak.EDU"  9-JAN-1992 12:26:24.70
To:	Multiple recipients of list ANU-NEWS <ANU-NEWS@VM1.NoDak.EDU>
CC:	
Subj:	GAWK programs to summarize NEWS_USAGE.LOG

Received: by AITGW.DECnet (utk-mail11 v1.5) ; Thu, 9 Jan 92 12:22:34 EST
Received: from vm1.NoDak.edu by aitgw.ge.com (5.65/GE Gateway 1.4)
	id AA19497; Thu, 9 Jan 92 05:42:45 -0500
Message-Id: <9201091042.AA19497@aitgw.ge.com>
Received: from NDSUVM1.BITNET by VM1.NoDak.EDU (IBM VM SMTP V2R2)
   with BSMTP id 0686; Thu, 09 Jan 92 04:42:42 CST
Received: from NDSUVM1.BITNET by NDSUVM1.BITNET (Mailer R2.07) with BSMTP id
 6951; Thu, 09 Jan 92 04:42:36 CST
Date:         Wed, 8 Jan 1992 12:00:40 GMT
Reply-To: goathunter%WKUVX1.BITNET@VM1.NoDak.EDU
Sender: ANU-NEWS Discussion <ANU-NEWS@VM1.NoDak.EDU>
From: goathunter%WKUVX1.BITNET@VM1.NoDak.EDU
Subject:      GAWK programs to summarize NEWS_USAGE.LOG
To: Multiple recipients of list ANU-NEWS <ANU-NEWS@VM1.NoDak.EDU>
 
Included below is a VMS_SHARE file containing a command procedure that uses
GAWK to produce summary reports from the NEWS logger patch that was posted
here a couple of months ago.  That patch is also included.
 
The patch causes a usage log file to be created, showing which users read
and posted to which groups.  My awk/.COM file produces reports like the
following:
 
NEWS_USAGE Summary for Jan 5
 
------------------------------------------------------
User                                  # Read  # Posted
------------                        --------  --------
NUGENT                                    15         0
SIMMONS                                    5         0
COOPER                                     4         0
WILDER                                     1         0
 
------------------------------------------------------
Newsgroup                             # Read  # Posted
---------                           --------  --------
alt.personals                             15         0
rec.games.misc                             4         0
rec.sport.cricket                          2         0
rec.sport.cricket.scores                   2         0
alt.sex.bondage                            1         0
comp.sys.apple2                            1         0
 
------------------------------------------------------
Total users:                  4
Total newsgroups:             6
Total messages read:         25 (newsgroups:    6)
Total messages posted:        0 (newsgroups:    0)
 
To use the program, you will need to get GAWK, Pat Rankin's VMS port of
GNU awk.  It's available via anonymous ftp from HAMLET.CALTECH.EDU,
RML2.SRI.COM, and the vmsnet.sources archive (via anonymous
ftp from black.cerritos.edu and white.cerritos.edu or by sending mail
with the body HELP and INDEX to MAILSERV@CERRITOS.EDU).
 
Hunter
------
Hunter Goatley, VMS Systems Programmer, Western Kentucky University
goathunter@WKUVX1.BITNET, 502-745-5251
 
$! ------------------ CUT HERE -----------------------
$ v='f$verify(f$trnlnm("SHARE_VERIFY"))'
$!
$! This archive created by VMS_SHARE Version 7.2-007  22-FEB-1990
$!   On  8-JAN-1992 06:50:52.22   By user GOATHUNTER (@WKUVX1.BITNET)
$!
$! This VMS_SHARE Written by:
$!    Andy Harper, Kings College London UK
$!
$! Acknowledgements to:
$!    James Gray       - Original VMS_SHARE
$!    Michael Bednarek - Original Concept and implementation
$!
$! TO UNPACK THIS SHARE FILE, CONCATENATE ALL PARTS IN ORDER
$! AND EXECUTE AS A COMMAND PROCEDURE  (  @name  )
$!
$! THE FOLLOWING FILE(S) WILL BE CREATED AFTER UNPACKING:
$!       1. NEWS_USAGE.COM;33
$!       2. NEWS_USAGE.PATCH;1
$!
$set="set"
$set symbol/scope=(nolocal,noglobal)
$f=f$parse("SHARE_TEMP","SYS$SCRATCH:.TMP_"+f$getjpi("","PID"))
$e="write sys$error  ""%UNPACK"", "
$w="write sys$output ""%UNPACK"", "
$ if f$trnlnm("SHARE_LOG") then $ w = "!"
$ ve=f$getsyi("version")
$ if ve-f$extract(0,1,ve) .ges. "4.4" then $ goto START
$ e "-E-OLDVER, Must run at least VMS 4.4"
$ v=f$verify(v)
$ exit 44
$UNPACK: SUBROUTINE ! P1=filename, P2=checksum
$ if f$search(P1) .eqs. "" then $ goto file_absent
$ e "-W-EXISTS, File ''P1' exists. Skipped."
$ delete 'f'*
$ exit
$file_absent:
$ if f$parse(P1) .nes. "" then $ goto dirok
$ dn=f$parse(P1,,,"DIRECTORY")
$ w "-I-CREDIR, Creating directory ''dn'."
$ create/dir 'dn'
$ if $status then $ goto dirok
$ e "-E-CREDIRFAIL, Unable to create ''dn'. File skipped."
$ delete 'f'*
$ exit
$dirok:
$ w "-I-PROCESS, Processing file ''P1'."
$ if .not. f$verify() then $ define/user sys$output nl:
$ EDIT/TPU/NOSEC/NODIS/COM=SYS$INPUT 'f'/OUT='P1'
PROCEDURE Unpacker ON_ERROR ENDON_ERROR;SET(FACILITY_NAME,"UNPACK");SET(
SUCCESS,OFF);SET(INFORMATIONAL,OFF);f:=GET_INFO(COMMAND_LINE,"file_name");b:=
CREATE_BUFFER(f,f);p:=SPAN(" ")@r&LINE_END;POSITION(BEGINNING_OF(b));
LOOP EXITIF SEARCH(p,FORWARD)=0;POSITION(r);ERASE(r);ENDLOOP;POSITION(
BEGINNING_OF(b));g:=0;LOOP EXITIF MARK(NONE)=END_OF(b);x:=ERASE_CHARACTER(1);
IF g=0 THEN IF x="X" THEN MOVE_VERTICAL(1);ENDIF;IF x="V" THEN APPEND_LINE;
MOVE_HORIZONTAL(-CURRENT_OFFSET);MOVE_VERTICAL(1);ENDIF;IF x="+" THEN g:=1;
ERASE_LINE;ENDIF;ELSE IF x="-" THEN IF INDEX(CURRENT_LINE,"+-+-+-+-+-+-+-+")=
1 THEN g:=0;ENDIF;ENDIF;ERASE_LINE;ENDIF;ENDLOOP;t:="0123456789ABCDEF";
POSITION(BEGINNING_OF(b));LOOP r:=SEARCH("`",FORWARD);EXITIF r=0;POSITION(r);
ERASE(r);x1:=INDEX(t,ERASE_CHARACTER(1))-1;x2:=INDEX(t,ERASE_CHARACTER(1))-1;
COPY_TEXT(ASCII(16*x1+x2));ENDLOOP;WRITE_FILE(b,GET_INFO(COMMAND_LINE,
"output_file"));ENDPROCEDURE;Unpacker;QUIT;
$ delete/nolog 'f'*
$ CHECKSUM 'P1'
$ IF CHECKSUM$CHECKSUM .eqs. P2 THEN $ EXIT
$ e "-E-CHKSMFAIL, Checksum of ''P1' failed."
$ ENDSUBROUTINE
$START:
$ create 'f'
X$ save_verify = 'f$verify(0)'
X$!
X$!  File:`09NEWS_USAGE.COM
X$!
X$!  Author:`09Hunter Goatley
X$!`09`09goathunter@WKUVX1.BITNET
X$!`09`09January 6, 1992
X$!
X$!  Abstract:
X$!
X$!`09This command procedure uses GAWK to produce usage summary reports
X$!`09for ANU-NEWS.  The file pointed to by the logical NEWS_USAGE_LOG
X$!`09is analyzed.
X$!
X$!`09Output is normally written to SYS$OUTPUT; it can be written to
X$!`09a file using:
X$!
X$!`09`09$ @NEWS_USAGE/OUTPUT=file.ext
X$!
X$!  Inputs:
X$!`09P1`09- Time (must be given, even when time is month)
X$!`09P2`09- DAY, MONTH, or ALL (default is DAY)
X$!`09P3`09- FULL or BRIEF (default is FULL)
X$!`09P4`09- ALPHA or COUNT (method for sorting users and groups,
X$!`09`09  default is ALPHA)
X$!
X$ on error then goto common_exit
X$ on contrl_y then goto common_exit
X$!
X$!  Get the date and determine the month and/or day for the reports.
X$!  Also, check other input parameters.
X$!
X$ date = f$time()
X$ if p1 .nes. "" then date = p1
X$ date = f$cvtime(date,"ABSOLUTE")
X$ months = "/Jan/Feb/Mar/Apr/May/Jun/Jul/Aug/Sep/Oct/Nov/Dec"
X$ cvdate = f$cvtime(date)
X$ month = f$element(f$extract(5,2,cvdate),"/",months)
X$ day = f$cvtime(date,"ABSOLUTE","DAY")
X$ vars = "-v ""month=''month'""
X$ if (p2 .eqs. "") .or. (f$extract(0,1,p2) .eqs. "D") then -
X`09vars = vars + " -v ""day=''day'""
X$ if (p2 .eqs. "ALL") then vars = ""
X$ full_report = 1
X$ if p3 .eqs. "BRIEF" then full_report = 0
X$ sort_method = 0
X$ if p4 .eqs. "COUNT" then sort_method = 1
X$ pid = f$getjpi("","PID")
X$!
X$!  Use GAWK to parse file, creating temporary file that will be sorted
X$!  and re-GAWKed to produce the final output file.
X$!
X$ gawk 'vars' -f sys$input news_usage_log >sys$scratch:newsutmp_'pid'.tmp
X$ DECK
X#
X#  AWK program to generate summaries from NEWS_MANAGER:NEWS_USAGE.LOG
X#
XBEGIN `7B
X`09if (length(month) == 0)`09`09#If no month is defined
X`09   valid_day = 1;`09`09#... they are all valid
X`09`7D
XNF == 16 `7B`09`09`09`09#For all header records
X`09if (length(month) != 0) `7B`09#Is there a month?
X`09   if (length(day) != 0) `7B`09# Is there a day?
X`09      if ((day == $3) && (month == $2))`09`09#If day and month match
X`09`09valid_day = 1;`09`09`09`09#... set valid_day
X`09      else
X`09`09valid_day = 0;
X`09   `7D
X`09   else `7B`09`09`09#Only a month was given
X`09      if (month == $2)
X`09`09valid_day = 1;
X`09      else
X`09`09valid_day = 0;
X`09      `7D
X`09`7D
X`09`7D
XNF == 16 && valid_day `7B
X`09if (length(start_date) == 0)
X`09   start_date = sprintf("%s %s %s",$1, $2, $3);
X`09user_read`5B$6`5D += $11;
X`09user_time`5B$6`5D += $8;
X`09user_post`5B$6`5D += $15;
X`09last_date = sprintf("%s %s %s",$1, $2, $3);
X`09`7D
XNF == 3 && valid_day `7B
X`09  group_read`5B$1`5D += $2
X`09  group_post`5B$1`5D += $3`7D
XEND`09`7B printf("0 %s\n", start_date);
X`09  printf("1 %s\n", last_date);
X`09 for (f in user_read) `7B
X`09`09if (user_read`5Bf`5D > max_user_read) `7B
X`09`09    max_user_read = user_read`5Bf`5D;
X`09`09    max_read_user = f;
X`09`09`7D
X`09`09if (user_post`5Bf`5D > max_user_post) `7B
X`09`09    max_user_post = user_post`5Bf`5D;
X`09`09    max_post_user = f;
X`09`09`7D
X`09`09printf("6 %-50s %8d %8d %8d\n",
X`09`09`09f, user_read`5Bf`5D, user_post`5Bf`5D, user_time`5Bf`5D);
X`09  `7D
X`09  for (f in group_read) `7B
X`09`09if (group_read`5Bf`5D > max_group_read) `7B
X`09`09    max_group_read = group_read`5Bf`5D;
X`09`09    max_read_group = f;
X`09`09`7D
X`09`09if (group_post`5Bf`5D > max_group_post) `7B
X`09`09    max_group_post = group_post`5Bf`5D;
X`09`09    max_post_group = f;
X`09`09`7D
X`09`09printf("7 %-50s %8d %8d\n", f, group_read`5Bf`5D, group_post`5Bf`5D);
X`09  `7D
X`09  printf("2 %s %d\n", max_read_user, max_user_read);
X`09  printf("3 %s %d\n", max_post_user, max_user_post);
X`09  printf("4 %s %d\n", max_read_group, max_group_read);
X`09  printf("5 %s %d\n", max_post_group, max_group_post);
X`09`7D
X$ EOD
X$ if .not.sort_method
X$ then`09sort_quals = ""
X$ else`09sort_quals = "/key=(pos:1,size:1,ascend)/key=(pos:54,size:8,descend
V)" -
X`09`09`09+ "/key=(pos:3,size:50,ascend)"
X$ endif
X$ sort'sort_quals' sys$scratch:newsutmp_'pid'.tmp sys$scratch:newsutmp_'pid'
V.tmp
X$ vars = ""
X$ if full_report then vars = "-v ""full=1""
X$ gawk 'vars' -f sys$input sys$scratch:newsutmp_'pid'.tmp
X$ DECK
X#
X#  Process sorted NEWS_USAGE summary file
X#
X/`5E0 / `7Bstart_date = sprintf("%s %s", $3, $4)`7D
X/`5E1 / `7B
X`09end_date = sprintf("%s %s", $3, $4);
X`09if (start_date == end_date)
X`09  printf("NEWS_USAGE Summary for %s\n\n", start_date);
X`09else
X`09  printf("NEWS_USAGE Summary for %s through %s\n\n",
X`09`09start_date, end_date);
X      `7D
X/`5E2 / `7Bmax_read_user = $2; max_user_read = $3`7D
X/`5E3 / `7Bmax_post_user = $2; max_user_post = $3;
X`09if(!full)`7B
X`09if (!max_user_read)
X`09   printf("No user read any messages\n")
X`09else
X`09   printf("User %s read %d message%s\n", toupper(max_read_user),
X`09`09`09max_user_read, (max_user_read == 1) ? "" : "s");
X`09if (!max_user_post)
X`09   printf("No user posted any messages\n")
X`09else
X`09   printf("User %s posted %d message%s\n", max_post_user,
X`09`09`09max_user_post, (max_user_post == 1) ? "" : "s");
X`09`7D
X      `7D
X/`5E4 / `7Bmax_read_group = $2; max_group_read = $3`7D
X/`5E5 / `7Bmax_post_group = $2; max_group_post = $3;
X`09if (!full)`7B
X`09if (!max_group_read)
X`09   printf("No messages in any group were read\n")
X`09else
X`09   printf("%d message%s read in group %s\n",
X`09`09`09max_group_read,
X`09`09`09(max_group_read == 1) ? " was" : "s were",
X`09`09`09max_read_group);
X`09if (!max_group_post)
X`09   printf("No messages were posted to any group\n")
X`09else
X`09   printf("%d message%s were posted to group %s\n",
X`09`09`09max_group_post,
X`09`09`09(max_group_post == 1) ? " was" : "s were",
X`09`09`09max_post_group);
X`09`7D
X`09if (full) `7B
X`09   printf("------------------------------------------------------\n");
X`09   printf("%-34s  %8s  %s\n","User","# Read","# Posted");
X`09   printf("%-34s  %8s  %s\n","------------","--------","--------");
X`09`7D
X      `7D
X/`5E6 / `7B
X`09if (full)
X`09    printf("%-34s  %8d  %8d\n", toupper($2), $3, $4);
X`09total_users++;
X      `7D
X/`5E7 / `7B
X    if (full)
X    if (head == 0) `7B
X    printf("\n------------------------------------------------------\n");
X    printf("%-34s  %8s  %s\n","Newsgroup","# Read","# Posted");
X    printf("%-34s  %8s  %s\n","---------","--------","--------");
X    head = 1;
X    `7D
X    total_groups++;
X    total_read += $3;
X    total_posted += $4;
X    if ($3)
X`09total_groups_read++;
X    if ($4)
X`09total_groups_posted++;
X    if (full)
X       printf("%-34s  %8d  %8d\n", $2, $3, $4);
X    `7D
XEND `7B
X    printf("\n------------------------------------------------------\n");
X`09printf("Total users:           %8d\n", total_users);
X`09printf("Total newsgroups:      %8d\n", total_groups);
X`09printf("Total messages read:   %8d (newsgroups: %4d)\n",
X`09`09`09total_read, total_groups_read);
X`09printf("Total messages posted: %8d (newsgroups: %4d)\n",
X`09`09`09total_posted, total_groups_posted);
X    `7D
X$ EOD
X$ common_exit:
X$`09delete/nolog/noconfirm sys$scratch:newsutmp_'pid'.tmp;*
X$`09exit f$verify(save_verify).or.1
$ CALL UNPACK NEWS_USAGE.COM;33 102240081
$ create 'f'
X*** news.c;1
X--- news.c
X**************
X*** 78,83
X  #endif
X
X    *nntp_node = '\0';
X
X    if ((lib$get_foreign(&tempdsc,0,&temp_len,0) & 1) && temp_len) `7B
X      temp`5Btemp_len`5D = '\0';
X--- 82,88 -----
X  #endif
X
X    *nntp_node = '\0';
X+   log_to_usage_file(0,0);
X
X    if ((lib$get_foreign(&tempdsc,0,&temp_len,0) & 1) && temp_len) `7B
X      temp`5Btemp_len`5D = '\0';
X*** newsfiles.c;1
X--- newsfiles.c
X**************
X*** 811,816
X    write_reg_file();
X    noscreen();
X    print_exit();
X  `7D
X  `0C
X  /*
X--- 813,819 -----
X    write_reg_file();
X    noscreen();
X    print_exit();
X+   log_to_usage_file(1,0);
X  `7D
X  `0C
X  /*
X*** newspost.c;1
X--- newspost.c
X**************
X*** 1098,1103
X    fclose(fpw);
X
X    c$cks(1);
X    if (*post_address) `7B
X      sprintf(err_oline,"Post to %s Moderator (%s)",
X              ga`5Bg`5D->grp_name,post_address);
X--- 1098,1104 -----
X    fclose(fpw);
X
X    c$cks(1);
X+   log_to_usage_file(-2,g);
X    if (*post_address) `7B
X      sprintf(err_oline,"Post to %s Moderator (%s)",
X              ga`5Bg`5D->grp_name,post_address);
X**************
X*** 1506,1511
X    fclose(fpr);
X
X    c$cks(1);
X    if (*post_address) `7B
X      sprintf(err_oline,"Post to %s Moderator (%s)",
X              ga`5Bg`5D->grp_name,post_address);
X--- 1507,1513 -----
X    fclose(fpr);
X
X    c$cks(1);
X+   log_to_usage_file(-2,g);
X    if (*post_address) `7B
X      sprintf(err_oline,"Post to %s Moderator (%s)",
X              ga`5Bg`5D->grp_name,post_address);
X*** newsread.c;1
X--- newsread.c
X**************
X*** 92,97
X    with a primary alpha key and an integer field. The opening and closing o
Vf the file
X    would be performed in NEWSFILES.C
X   */
X      if (ga`5Bg`5D->grp_unread > 0) `7B
X        --ga`5Bg`5D->grp_unread;
X        screen_update_gread(g);
X--- 92,98 -----
X    with a primary alpha key and an integer field. The opening and closing o
Vf the file
X    would be performed in NEWSFILES.C
X   */
X+     log_to_usage_file(-1,g);
X      if (ga`5Bg`5D->grp_unread > 0) `7B
X        --ga`5Bg`5D->grp_unread;
X        screen_update_gread(g);
X*** newsutility.c;1
X--- newsutility.c
X**************
X*** 3018,3023
X      close_mail_file();
X      noscreen();
X      print_exit();
X      exit(1);
X  `7D
X  `0C
X--- 3024,3030 -----
X      close_mail_file();
X      noscreen();
X      print_exit();
X+     log_to_usage_file(1,0);
X      exit(1);
X  `7D
X  `0C
X**************
X*** 3269,3272
X      cur_up_itm(curr_g,lines,1);
X      `7D
X    return(0);
X  `7D
X--- 3276,3395 -----
X      cur_up_itm(curr_g,lines,1);
X      `7D
X    return(0);
X+ `7D
X+ #include lnmdef
X+ #include psldef
X+
X+ do_abort()
X+ `7B
X+     sysprv();
X+     sys$close(&itmfab);
X+     sys$close(&grpfab);
X+     nosysprv();
X+     close_hist_file();
X+     close_nntp_file();
X+     close_mail_file();
X+     noscreen();
X+     log_to_usage_file(1,0);
X+     exit(1);
X+ `7D
X+
X+ static char *dat()
X+ `7B
X+   static char datum`5B32`5D;
X+   struct tm *stm;
X+   int cur_time;
X+   char *p;
X+
X+   time(&cur_time);
X+   p = ctime(&cur_time);
X+   strncpy(datum, p, 19);
X+   return(datum);
X+ `7D
X+
X+ log_to_usage_file(flag,group)
X+   int flag;`09`09`09`09/*  0 - initialize data areas */
X+ `09`09`09`09`09/*  1 - dump data to logging file */
X+ `09`09`09`09`09/* -1 - read article from group */
X+ `09`09`09`09`09/* -2 - post article to group */
X+   int group;`09`09`09`09/* group number for read/post */
X+ `7B                                       /* logging declarations */
X+   FILE *fpl;
X+   int end_time, g, i;
X+   static int start_time, max_index, read_count, post_count;
X+   static int *group_nums, *read_groups, *post_groups;
X+
X+   char exec_mode = PSL$C_EXEC, translate_value`5B256`5D;
X+   unsigned int trnlist`5B4`5D;
X+   unsigned short len = 0;
X+
X+   if (flag == 0) `7B
X+     time(&start_time);
X+     max_index = 0;
X+     read_count = 0;
X+     post_count = 0;
X+     group_nums = malloc(100 * sizeof(int));
X+     read_groups = malloc(100 * sizeof(int));
X+     post_groups = malloc(100 * sizeof(int));
X+     return;
X+     `7D
X+
X+   if (group) group = ga`5Bgroup`5D->grp_num;
X+   if (flag < 0) `7B
X+     for (g = 0; g < max_index; g++)
X+       if (group_nums`5Bg`5D == group) break;
X+     if (g == max_index) `7B
X+       if (max_index == 100) g = -1;
X+       else `7B
X+         max_index++;
X+         group_nums`5Bg`5D = group;
X+         read_groups`5Bg`5D = 0;
X+         post_groups`5Bg`5D = 0;
X+         `7D
X+       `7D
X+     `7D
X+
X+   if (flag == -1) `7B
X+     read_count++;
X+     if (g >= 0) read_groups`5Bg`5D++;
X+     return;
X+     `7D
X+
X+   if (flag == -2) `7B
X+     post_count++;
X+     if (g >= 0) post_groups`5Bg`5D++;
X+     return;
X+     `7D
X+
X+   if (!no_priv()) return;
X+
X+                    /*    Set up item list for exec mode logical names */
X+   trnlist`5B0`5D = (LNM$_STRING << 16) + LNM$C_NAMLENGTH;
X+   trnlist`5B1`5D = translate_value;
X+   trnlist`5B2`5D = &len;
X+   trnlist`5B3`5D = 0;
X+
X+   if (!(sys$trnlnm(0,c$dsc(LOG_TABLE),c$dsc("NEWS_USAGE_LOG"),&exec_mode,&
Vtrnlist) & 1))
X+       return;
X+
X+   translate_value`5Blen`5D = '\0';
X+   sysprv();
X+   if (!(fpl = fopen(translate_value,"a"))) `7B
X+     sleep(1);
X+     fpl = fopen(translate_value,"a");
X+     `7D
X+   if (fpl) `7B
X+     time(&end_time);
X+     end_time -= start_time;
X+     if (end_time < 0) end_time = 0;
X+     fprintf(fpl,"%s: User %s - %d minutes - %d articles read - %d posted\n
V",
X+       dat(), usr_username, (end_time + 30) / 60, read_count, post_count);
X+     for (i = 0; i < max_index; i++) `7B
X+       g = ga_locate(group_nums`5Bi`5D);
X+       if (!g) continue;
X+       fprintf(fpl,"\t%s %d %d\n",ga`5Bg`5D->grp_name,read_groups`5Bi`5D,po
Vst_groups`5Bi`5D);
X+       `7D
X+     fclose(fpl);
X+     `7D
X+   nosysprv();
X  `7D
$ CALL UNPACK NEWS_USAGE.PATCH;1 290216472
$ v=f$verify(v)
$ EXIT
