Subject: v06i101: Count unread news articles (newscnt) Newsgroups: mod.sources Approved: rs@mirror.UUCP Submitted by: ihnp4!inuxd!gat Mod.sources: Volume 6, Issue 101 Archive-name: newscnt This program has been used here for several months and seems to be stable. I haven't had the opportunity to try it out on other UNIXes. Glen A. Taylor AT&T (Consumer Products Division) Indianapolis, IN (317) 845-3709 ihnp4!inuxd!gat [ With Glen's permission, I added the code to use getopt rather than scanargs, and the "#ifdef BSD" code; I don't think I broke anything. Note that this program uses strtok() -- if it's not in your library, the mod.sources archives have two public-domain implementations of it and the other string functions. I haven't tested the program as extensively as I might like to, because its utility to me is limited; we use notes here. --r$ ] #!/bin/sh # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # Wrapped by rs@mirror.UUCP on Sun Aug 3 19:56:49 EDT 1986 # Contents: README Makefile newscnt.1 newscnt.c scanargs.c echo x - README sed 's/^XX//' > "README" <<'@//E*O*F README//' XXHere is a little utility that I hope others will find as useful as I XXhave. Ever since I started to read the net, I have wanted a means to XXdetermine how much news is waiting for me before I dive into it. When XXthe "wn" program came across net.sources a couple months ago, I thought XXmy problem was solved. Unfortunately, that program didn't do what I XXwanted; I am not interested in knowing all of the unread news on the XXsystem but only the news groups that I read. Consequently I decided to XXwrite "newscnt." XXNewscnt makes use of your .newsrc file and the system's XX/usr/lib/news/active file to determine your unread news in much the XXsame way that readnews or vnews does. It looks at your "option" line XXin .newsrc to determine the news groups that interest you and then XXbuild the structures it needs to count your unread articles. By XXdefault, newscnt only returns a total, but with the "-a" option it will XXgive you a breakdown by newsgroup. A manual page is included that XXdescribes all the bells and whistles. XXNewscnt uses the "scanargs" argument parser that was distributed quite XXsome time ago on this net. I have included a copy in this distribution XXfor those who don't have one. XXAny bug reports, suggestions for improvement, other complaints or even XXpraise should be sent to: XX Glen A. Taylor XX ihnp4!inuxd!gat XX AT&T (Consumer Products Division) XX P.O. Box 1008 XX Indianapolis, IN 46206 (317) 845-3709 @//E*O*F README// chmod u=rw,g=rw,o=rw README echo x - Makefile sed 's/^XX//' > "Makefile" <<'@//E*O*F Makefile//' XX## If on a BSD system, enable the next line. Also, make arrangements to XX## get strtok(3) pulled in somehow. XX#BSD = -DBSD XX## If you want getopt, enable this line: XXGETOPT = -DUSE_GETOPT XX## You may have to have something like this line, if getopt isn't in libc.a XX#GETOBJ = -lgetopt XX## If no getopt, enable this line: XX#SCAN = scanargs.o XXCFLAGS=$(BSD) $(GETOPT) XXnewscnt: newscnt.o $(SCAN) XX cc -o newscnt newscnt.o $(SCAN) $(GETOBJ) @//E*O*F Makefile// chmod u=rw,g=rw,o=rw Makefile echo x - newscnt.1 sed 's/^XX//' > "newscnt.1" <<'@//E*O*F newscnt.1//' XX.TH NEWSCNT 1 LOCAL XX.SH NAME XXnewscnt \- count unread network news based on user's newsgroup XXchoices XX.SH SYNOPSIS XX.B newscnt XX[ XX.BR - { sta } XX] [ XX.B -x XX] [ XX.B -f XXalt_.newsrc ] [ XX.B -n XXnewsgrp ...] XX.SH DESCRIPTION XX.I Newscnt XXprovides a count of unread network news. By default, the user's .newsrc XXfile is examined to determine a list of newsgroups. These newsgroups XXare then matched against the list of articles that the user has read XXand a count of available unread news articles is determined. The XXprogram writes to stdout either "No news." if there are no unread news XXarticles, or "\f2nnn\fP news articles." to indicate the number of XXunread articles. Options XXare provided for altering the format in which the count(s) are XXreturned and the inputs the program works from. XX.P XXThe following options are supported: XX.TP .5i XX\f3-\fP{\f3sta\fP} XXThese three options, only one of which may be specified at a time, XXaffect the output format of the program. XX.RS .5i XX.TP .5i XX\f3s\fP XXThe \f2silent\fP option -- nothing is written to stdout. Only the XXprogram's normal return codes are provided, e.g. a \f31\fP if there is XXno unread news, a \f30\fP if there is unread news, or a \f3-1\fP if XXthere was an error in the processing. XX.TP .5i XX\f3t\fP XXThe \f2terse\fP option -- only the total number of unread news XXarticles (including zero if there are no unread news articles) is XXwritten to stdout. No other text is written. XX.TP .5i XX\f3a\fP XXThe \f2all\fP option -- in addition to a report of the total number of XXunread news articles, the program writes a line of the form XX"\f2newsgroup\fP: \f2nnn\fP articles" for each newsgroup having one or XXmore unread news articles. XX.RE XX.TP .5i XX\f3-x\fP XXThis flag causes the program to ignore the articles that the user has XXalready read in each newsgroup and report the total number of articles XXavailable in the newsgroup. The effect is similar to the \f3-x\fP XXflag on most news reading programs. XX.TP .5i XX\f3-f \f2alt_.newsrc\fP XXWhen this option is specified, the file \f2alt_.newsrc\fP is used XXinstead of the user's \f2$HOME/.newsrc\fP file. XX.TP .5i XX\f3-n \f2newsgrps\fP ... XXWhen this option is specified, the \f2newsgrps\fP specified on the XXcommand line are reported on. By default, the program searches XXthrough the specified or default .newsrc file for a line that begins XXwith the word \f2options\fP. This \f2options\fP line is parsed for the XXlist of \f2newsgrps\fP following a \f3-n\fP argument. Any XX\f2newsgrp\fP, either from the command line or from the .newsrc file, XXprefaced with an exclamation point will be specifically excluded from XXthe computation. This is useful for excluding "embedded" newsgroups. XX(\f3NOTE\fP: A \f2newsgrp\fP is taken as the "prefix" to a set of XXnewsgroups and all newsgroups with that prefix are automatically XXincluded in the count.) XX.SH FILES XX/usr/lib/news/active XX.br XX$HOME/.newsrc XX.SH CAVEATS XXThis program makes use of the /usr/lib/news/active file to determine XXthe available news items and not the actual news directory's contents. XXConsequently, its answers may be off slightly if the active file does not XXaccurately track the available news articles. If you are told XXyou have unusually large amounts of unread news, use the -a option to XXpinpoint the newsgroup(s) with unusual counts XXand then check to see if there are unexpired XXarticles causing an unusually large "range" in the active file. XX.P XXThe .newsrc format is interpreted according to the author's understanding of XXthat file's format. Variations in how this file is interpreted may exist. XX.SH AUTHOR XX.nf XXGlen A. Taylor XXAT&T (Consumer Products Divsion) XXMay 1986 XX.fi @//E*O*F newscnt.1// chmod u=rw,g=rw,o=rw newscnt.1 echo x - newscnt.c sed 's/^XX//' > "newscnt.c" <<'@//E*O*F newscnt.c//' XX/* newscnt -- a program to enumerate unread net.news articles * XX * * XX * Glen A. Taylor (AT&T-IS) May 1986 * XX * * XX * Added the code inside the BSD and USE_GETOPT #ifdef's * XX * Rich $alz (Mirror Systems) June 1986 * XX * */ XXstatic char sccsid[] = "@(#)newscnt.c 1.2"; XX#define BSD XX#define DEBUG XX#include XX#ifdef BSD XX#include XXchar *strtok(); XX#else XX#include XX#endif XX#include XX#include XX#define FALSE 0 XX#define TRUE 1 XX#define SILENT 4 XX#define TERSE 2 XX#define ALL 1 XX#define ERROR -1 XX#define NEWS 0 XX#define NONEWS 1 XX#define MAXLENG 512 XX#define MAXGRPS 128 XX#define ACTIVE "/usr/lib/news/active" XX#define NEWSRC ".newsrc" XX#ifdef BSD XX#define USER_NAME "USER" XX#else XX#define USER_NAME "LOGNAME" XX#endif XXstruct grp { XX char *name; /* news group name */ XX long lowest; /* lowest article number */ XX long highest; /* highest article number */ XX int done; /* to indicate this element has been processed */ XX struct grp *ptr; /* ptr. to next element */ XX}; XXtypedef struct grp *GRPTR; XXchar *namevec[MAXGRPS]; /* array of newsgroup name strings */ XXmain(argc,argv) XXint argc; XXchar **argv; XX{ XX char *alt_newsrc, **newsgrps, *malloc(); XX int silent, terse, all, flags, fflag, nflag, xflag, nr_newsgrps; XX FILE *fpnewsrc, *fpactive; XX int count = 0; XX GRPTR listhead = NULL; XX /* Parse arguments and establish working parameters */ XX#ifdef USE_GETOPT XX { XX char c; XX extern char *optarg; XX extern int optind; XX nr_newsgrps = silent = terse = all = xflag = fflag = nflag = flags = 0; XX while ((c = getopt(argc, argv, "staxf:n:")) != EOF) XX switch (c) { XX default: XXUsage: XX fprintf(stderr, XX "usage: %s [-{sta}] [-x] [-f alt_.newsrc] -n newsgrp...", XX argv[0]); XX exit(1); XX case 's': silent = 1; break; XX case 't': terse = 1; break; XX case 'a': all = 1; break; XX case 'x': xflag = 1; break; XX case 'f': fflag = 1; alt_newsrc = optarg; break; XX case 'n': nflag = 1; nr_newsgrps += addgrp(optarg); break; XX } XX if (silent + terse + all > 1) XX goto Usage; XX for (argv += optind; *argv; argv++) XX nr_newsgrps += addgrp(argv); XX } XX#else /* !USE_GETOPT */ XX if (!scanargs(argc,argv, "% sta%- x%- f%-alt_.newsrc!s n%-newsgrp!*s", XX &flags, &xflag, &fflag, &alt_newsrc, &nflag, &nr_newsgrps, &newsgrps)) XX exit(1); XX silent = flags & SILENT; XX terse = flags & TERSE; XX all = flags & ALL; XX#endif /* USE_GETOPT */ XX /* Open the .newsrc file */ XX if (fflag) { /* use the file name the user supplied */ XX fpnewsrc = fopen(alt_newsrc,"r"); XX } XX else { /* determine the user's .newsrc file and open */ XX { XX char *fname[MAXLENG]; XX char *uname, *getenv(); XX struct passwd *passptr, *getpwnam(); XX uname = getenv(USER_NAME); /* get user's login name */ XX passptr = getpwnam(uname); /* get user's home dir */ XX /* construct .newsrc path */ XX strcpy(fname, passptr -> pw_dir); XX strcat(fname, "/"); XX strcat(fname, NEWSRC); XX fpnewsrc = fopen(fname,"r"); XX } XX } XX /* Derive array of newsgroup names to search for */ XX if (!nflag) getgrps(fpnewsrc, &nr_newsgrps, &newsgrps); XX#ifdef DEBUG XX { int i; XX printf("Number of newsgroups = %d\n", nr_newsgrps); XX for (i = 0; i < nr_newsgrps; i++) printf("%s\n", newsgrps[i]); XX } XX#endif XX if (nr_newsgrps == 0) exit (ERROR); /* can't do anything! */ XX /* Open the ACTIVE newsgroup file */ XX if ((fpactive = fopen(ACTIVE, "r"))==NULL) XX exit(ERROR); /* cannot proceed w/o ACTIVE file */ XX /* Go through the ACTIVE file and make linked list of all XX newsgroups matching elements of the "newsgrps" array */ XX { XX char gname[MAXLENG]; /* tmp to hold active group name */ XX long low, high; /* tmps to hold active group low & high numbers */ XX int i, j; XX char *sp, junk[10]; XX GRPTR current, next; XX while (fscanf(fpactive,"%s %ld %ld %s\n", gname, &high, &low, junk) XX != EOF) { /* read the entire ACTIVE file */ XX /* Compare gname to every name in newsgrps */ XX for (i=0; i < nr_newsgrps; i++) { XX sp = newsgrps[i]; XX j = strlen(sp); XX /* check for a group to be explicitly skipped */ XX if (*sp == '!') { XX sp++; XX if (strncmp(gname,sp,j-1) == 0) break; XX } XX /* otherwise check for a positive match */ XX else if (strncmp(gname,sp,j) == 0) { XX /* got a matching group so allocate new list element */ XX next = (struct grp *) malloc(sizeof (struct grp)); XX if (!listhead) {/* first element */ XX listhead = next; XX current = next; XX } XX else { XX current -> ptr = next; /* link on this element */ XX current = next; XX } XX /* fill in the list element structure */ XX j = strlen(gname) + 1; /* get length of group name */ XX current -> name = strcpy(malloc(j), gname); XX current -> lowest = low; XX current -> highest = high; XX current -> done = FALSE; XX current -> ptr = NULL; XX /* terminate inner loop -- proceed to next group */ XX break; XX } XX } XX } XX } XX#ifdef DEBUG XX { XX GRPTR lptr; XX printf("\nList of relevant active newsgroups:\n"); XX lptr = listhead; XX while (lptr != NULL) { XX printf("%s %d %d\n",lptr->name,lptr->highest,lptr->lowest); XX lptr = lptr->ptr; XX } XX } XX#endif XX /* Work down the linked list and compare to .newsrc entries */ XX rewind(fpnewsrc); XX { XX GRPTR next; XX char line[MAXLENG], *gname, *range; XX int ignore, cnt; XX while(fgets(line, MAXLENG, fpnewsrc) != NULL){ XX gname = line; /* name starts in first char position */ XX range = line; /* find the "range" list */ XX while (*range != ':' && *range != '!' && *range != '\0') XX range++; /* : and ! are the two legal name delimiters */ XX ignore = *range == '!' ? TRUE : FALSE; /* do we ignore? */ XX *range++ = '\0'; /* delimit gname from range */ XX /* Search the list for this newsgroup */ XX next = listhead; XX while (next != NULL) { XX if (strcmp(gname,next -> name) == 0) {/* Found it */ XX if (!ignore) { XX cnt = ckrange(next, range, xflag); XX count = count + cnt; XX if (all && cnt) printf("%s: %d articles\n",gname,cnt); XX } XX next -> done = TRUE; XX break; XX } XX next = next -> ptr; XX } XX } XX /* go through list one more time and get those that aren't "done" */ XX next = listhead; XX while (next != NULL) { XX if (! next -> done) { XX cnt = next -> highest - next -> lowest; XX if (cnt < 0) cnt = 0; XX count = count + cnt; XX /* print the nonzero entries if "all" were requested */ XX if (all && cnt) printf("%s: %d articles\n",next->name,cnt); XX } XX next = next -> ptr; XX } XX } XX /* Print final total */ XX if (silent) exit( count? NEWS : NONEWS); XX if (terse) printf("%d\n", count); XX else if (count) printf("%d news articles.\n",count); XX else printf("No news.\n"); XX exit(count? NEWS : NONEWS); XX} XX#ifdef USE_GETOPT XX/* addgrp -- add a group list to the namevec global array. XX returns count of groups added. */ XXaddgrp(p) XXchar *p; XX{ XX static char SEPARATORS[] = " \t\n,"; XX static int cnt; XX char *ptr; XX int i; XX ptr = strtok(p, SEPARATORS); XX for (i = 0; ptr = strtok(NULL, SEPARATORS); i++) XX namevec[cnt++] = strcpy(malloc(strlen(ptr) + 1), ptr); XX return(i); XX} XX#endif /* USE_GETOPT */ XX/* getgrps -- reads the .newsrc file, finds the "option -n ... " news XX group list, and copies news groups into an array of XX strings. */ XXgetgrps(fp, num, names) XXFILE *fp; XXint *num; XXchar ***names; XX{ XX char line[MAXLENG], *ptr; XX int optflag = FALSE, nflag = FALSE; XX *names = namevec; XX *num = 0; XX while (fgets(line,MAXLENG,fp) != NULL ) { XX if (!optflag && strncmp(line,"option",6) == 0) { XX /* found an option line */ XX optflag = TRUE; XX nflag = FALSE; XX ptr = strtok(line," \t\n"); XX ptr = strtok(NULL," \t\n"); /* skip "option" */ XX } XX else if (optflag && isspace(line[0])) { XX /* found a continuation line */ XX ptr = strtok(line," \t\n"); XX } XX else { XX optflag = FALSE; XX nflag = FALSE; XX } XX /* Parse the options line */ XX if (optflag) { XX while (!nflag && ptr != NULL) { XX if (strcmp(ptr,"-n") == 0) { XX nflag = TRUE; XX ptr = strtok(NULL, " \t\n"); /* consume "-n" */ XX break; XX } XX ptr = strtok(NULL, " \t\n"); /* get first news group */ XX } XX while (nflag && ptr != NULL) { XX /* copy this news group into "newsgrps" */ XX namevec[*num] = strcpy(malloc(strlen(ptr)+1), ptr); XX *num += 1; XX ptr = strtok(NULL, " \t\n"); XX } XX } XX } XX} XX/* ckrange -- compare a range specification against a high count / low XX count and return the number of unread articles */ XXint XXckrange(ptr, range, flag) XXGRPTR ptr; XXchar *range; XXint flag; XX{ XX long low, high, nxthi, nxtlo; XX int i, extent, count; XX char *bitmap, *nextseg(), *rp; XX#ifdef DEBUG XX printf("<*> ckrange: \n\tname -- %s\n\thigh = %d\n\tlow = %d\n", XX ptr->name, ptr->highest, ptr->lowest); XX printf("\trange -- %s\n", range); XX#endif XX /* get low and high values for the newsgroup */ XX low = ptr -> lowest; XX high = ptr -> highest; XX if (low > high) return (0); /* this one is screwy! */ XX if (flag) { /* ignore range info */ XX count = high - low; XX if (count < 0 ) count = 0; XX return (count); XX } XX /* set up a bitmap to do the computation */ XX extent = high - low + 1; XX if (extent <= 0) return (0); XX bitmap = malloc(extent); XX if (bitmap == NULL) exit(ERROR); XX for (i=0; i < extent; i++) bitmap[i] = '*'; /* init. bitmap */ XX /* set bits (bytes, actually) for each read article */ XX rp = range; XX while ((rp = nextseg(&nxthi,&nxtlo,rp)) != NULL) { XX#ifdef DEBUG XX printf("<*> nexthi = %d, nextlo = %d\nnext seg = `%s'\n", XX nxthi, nxtlo, rp); XX#endif XX /* check that the ones read intersect the ones avail */ XX if (nxthi < low || nxtlo > high) continue; XX /* adjust the range markers to the intersection */ XX nxthi = (high > nxthi) ? nxthi : high; XX nxtlo = (low < nxtlo) ? nxtlo : low; XX /* mark the articles that were read */ XX if (nxtlo == nxthi) bitmap[nxtlo-low] = ' '; XX else for (i=nxtlo-low; i <= nxthi-low; i++) bitmap[i] = ' '; XX } XX /* count the set bits & go home */ XX count = 0; XX for (i=0; i < extent; i++) if (bitmap[i] == '*') count++; XX free(bitmap); XX return (count); XX} XXchar * XXnextseg(high, low, str) XXlong *high, *low; XXchar *str; XX{ XX char *p1, *retval; XX long val = 0L; XX int hyphen = FALSE, more = TRUE; XX if (*str == '\0') return(NULL); XX p1 = str; XX while (more) { XX switch (*p1) { XX case '0': XX case '1': XX case '2': XX case '3': XX case '4': XX case '5': XX case '6': XX case '7': XX case '8': XX case '9': XX val = val * 10 + (*p1 - '0'); XX break; XX case '-': XX *low = val; XX val = 0L; XX hyphen = TRUE; XX break; XX case ' ': XX case ',': XX case '\n': XX more = FALSE; XX retval = ++p1; XX *high = val; XX if (!hyphen) *low = val; XX break; XX case '\0': XX more = FALSE; XX retval = NULL; XX *high = val; XX if (!hyphen) *low = val; XX return (retval); XX } XX p1++; XX } XX return (retval); XX} @//E*O*F newscnt.c// chmod u=rw,g=rw,o=rw newscnt.c echo x - scanargs.c sed 's/^XX//' > "scanargs.c" <<'@//E*O*F scanargs.c//' XX/* XX * $Header: scanargs.c,v 1.3 83/05/22 02:21:32 thomas Exp $ XX * Version 7 compatible XX * Argument scanner, scans argv style argument list. XX * XX * Some stuff is a kludge because sscanf screws up XX * XX * Gary Newman - 10/4/1979 - Ampex Corp. XX * XX * Modified by Spencer W. Thomas, Univ. of Utah, 5/81 to XX * add args introduced by a flag, add qscanargs call, XX * allow empty flags. XX * XX * Compiling with QUICK defined generates 'qscanargs' == XX * scanargs w/o floating point support; avoids huge size XX * of scanf. XX * XX * If you make improvements we'd like to get them too. XX * Jay Lepreau lepreau@utah-20, decvax!harpo!utah-cs!lepreau XX * Spencer Thomas thomas@utah-20, decvax!harpo!utah-cs!thomas XX * XX * (I know the code is ugly, but it just grew, you see ...) XX * XX * Modified by: Spencer W. Thomas XX * Date: Feb 25 1983 XX * 1. Fixed scanning of optional args. Now args introduced by a flag XX * must follow the flag which introduces them and precede any other XX * flag argument. It is still possible for a flag introduced XX * argument to be mistaken for a "bare" argument which occurs XX * earlier in the format string. This implies that flags may not XX * be conditional upon other flags, and a message will be generated XX * if this is attempted. XX * XX * 2. Usage message can be formatted by inserting newlines, tabs and XX * spaces into the format string. This is especially useful for XX * long argument lists. XX * XX * 3. Added n/N types for "numeric" args. These args are scanned XX * using the C language conventions - a number starting 0x is XX * hexadecimal, a number starting with 0 is octal, otherwise it is XX * decimal. XX */ XX#include XX#include XX#include XXtypedef char bool; XX/* XX * An explicit assumption is made in this code that all pointers look XX * alike, except possible char * pointers. XX */ XXtypedef int *ptr; XX#define YES 1 XX#define NO 0 XX#define ERROR(msg) {fprintf(stderr, "msg\n"); goto error; } XX/* XX * Storage allocation macros XX */ XX#define NEW( type, cnt ) (type *) malloc( (cnt) * sizeof( type ) ) XX#define RENEW( type, ptr, cnt ) (type *) realloc( ptr, (cnt) * sizeof( type ) ) XXstatic char * prformat(); XXstatic bool isnum(); XX/* XX * Argument list is (argc, argv, format, ... ) XX */ XX#ifndef QUICK XXscanargs ( va_alist ) XX#else XXqscanargs ( va_alist ) XX#endif XXva_dcl XX{ XX int argc; /* Actual arguments */ XX char **argv; XX char *format; XX va_list argl; XX register check; /* check counter to be sure all argvs XX are processed */ XX register char *cp; XX register cnt; XX int optarg = 0; /* where optional args start */ XX int nopt; XX char tmpflg, /* temp flag */ XX typchr; /* type char from format string */ XX char c; XX bool * arg_used; /* array of flags */ XX char * malloc(); XX ptr aptr; /* pointer to return loc */ XX bool required; XX int excnt; /* which flag is set */ XX bool exflag; /* when set, one of a set of exclusive XX flags is set */ XX bool list_of; /* set if parsing off a list of args */ XX bool comma_list; /* set if AT&T style multiple args */ XX int * cnt_arg; /* where to stuff list count */ XX int list_cnt; /* how many in list */ XX /* These are used to build return lists */ XX char ** strlist; XX int * intlist; XX long * longlist; XX float * fltlist; XX double *dbllist; XX char *ncp; /* remember cp during flag scanning */ XX#ifndef QUICK XX char *cntrl; /* control string for scanf's */ XX char junk[2]; /* junk buffer for scanf's */ XX cntrl = "% %1s"; /* control string initialization for XX scanf's */ XX#endif XX va_start( argl ); XX argc = va_arg( argl, int ); XX argv = va_arg( argl, char ** ); XX format = va_arg( argl, char * ); XX arg_used = NEW( bool, argc ); XX if (arg_used == NULL) XX { XX fprintf(stderr, "malloc failed in scanargs, exiting\n"); XX exit(-1); XX } XX else XX { XX for (cnt=0; cnt 1 && !arg_used[argc-1] ) XX argc--; /* find last used argument */ XX *va_arg( argl, int * ) = argc; XX break; XX case '-': /* argument is flag */ XX if (optarg > 0) XX ERROR(Format error: flag conditional on flag not allowed); XX /* go back to label */ XX ncp = cp-1; /* remember */ XX cp -= 3; XX for (excnt = exflag = 0 XX ; *cp != ' ' && !(*cp=='-' &&(cp[-1]=='!'||cp[-1]=='%')); XX (--cp, excnt++)) XX { XX for (cnt = optarg+1; cnt < argc; cnt++) XX { XX /* flags all start with - */ XX if (*argv[cnt] == '-' && !arg_used[cnt] && XX !isdigit(argv[cnt][1])) XX if (*(argv[cnt] + 1) == *cp) XX { XX if (*(argv[cnt] + 2) != 0) XX ERROR (extra flags ignored); XX if (exflag) XX ERROR (more than one exclusive flag chosen); XX exflag++; XX required = NO; XX check += cnt; XX arg_used[cnt] = 1; XX nopt = cnt; XX *va_arg( argl, int *) |= (1 << excnt); XX break; XX } XX } XX } XX if (required) XX ERROR (flag argument missing); XX cp = ncp; XX /* XX * If none of these flags were found, skip any XX * optional arguments (in the varargs list, too). XX */ XX if (!exflag) XX { XX va_arg( argl, int * ); /* skip the arg, too */ XX while (*++cp && ! isspace(*cp)) XX if (*cp == '!' || *cp == '%') XX { XX if ( *++cp == '*' || *cp == ',' ) XX { XX cp++; XX va_arg( argl, int * ); XX } XX /* XX * Assume that char * might be a XX * different size, but that all XX * other pointers are same size. XX */ XX if ( *cp == 's' ) XX va_arg( argl, char * ); XX else XX va_arg( argl, ptr ); XX } XX } XX else XX { XX optarg = nopt; XX cp++; /* skip over - */ XX } XX break; XX case 's': /* char string */ XX case 'd': /* decimal # */ XX case 'o': /* octal # */ XX case 'x': /* hexadecimal # */ XX case 'n': /* "number" in C syntax */ XX#ifndef QUICK XX case 'f': /* floating # */ XX#endif XX case 'D': /* long decimal # */ XX case 'O': /* long octal # */ XX case 'X': /* long hexadecimal # */ XX case 'N': /* long number in C syntax */ XX#ifndef QUICK XX case 'F': /* double precision floating # */ XX#endif XX for (cnt = optarg+1; cnt < argc; cnt++) XX { XX ncp = argv[cnt]; XX if ( isnum( ncp, typchr, comma_list ) ) XX { XX if ( typchr == 's' ) /* string? */ XX continue; /* don't want numbers, then */ XX } XX else if ( *ncp == '-' ) XX if ( optarg > 0 ) /* end optional args? */ XX { XX /* Eat the arg, too, if necessary */ XX if ( list_cnt == 0 ) XX if ( typchr == 's' ) XX va_arg( argl, char * ); XX else XX va_arg( argl, ptr ); XX break; XX } XX else XX continue; XX else if ( typchr != 's' ) XX continue; /* not number, keep looking */ XX XX /* XX * Otherwise usable argument may already XX * be used. (Must check this after XX * checking for flag, though.) XX */ XX if (arg_used[cnt]) continue; XX /* XX * If it's a comma-and-or-space-separated XX * list then count how many, and separate XX * the list into an array of strings. XX */ XX if ( comma_list ) XX { XX register char * s; XX int pass; XX /* XX * On pass 0, just count them. On XX * pass 1, null terminate each string XX */ XX for ( pass = 0; pass <= 1; pass++ ) XX { XX for ( s = ncp; *s != '\0'; ) XX { XX if ( pass ) XX strlist[list_cnt] = s; XX while ( (c = *s) != '\0' && c != ' ' && XX c != '\t' && c != ',' ) XX s++; XX if ( pass ) XX *s = '\0'; XX list_cnt++; /* count separators */ XX /* XX * Two commas in a row give a null XX * string, but two spaces XX * don't. Also skip spaces XX * after a comma. XX */ XX if ( c != '\0' ) XX while ( *++s == ' ' || *s == '\t' ) XX ; XX } XX if ( pass == 0 ) XX { XX strlist = NEW( char *, list_cnt ); XX list_cnt = 0; XX } XX } XX } XX else if ( list_of ) XX list_cnt++; /* getting them one at a time */ XX /* XX * If it's either type of list, then alloc XX * storage space for the returned values XX * (except that comma-separated string XX * lists already are done). XX */ XX if ( list_of ) XX { XX if ( list_cnt == 1 || comma_list ) XX switch( typchr ) XX { XX case 's': XX if ( !comma_list ) XX strlist = NEW( char *, 1 ); XX aptr = (ptr) &strlist[0]; XX break; XX case 'n': XX case 'd': XX case 'o': XX case 'x': XX intlist = NEW( int, list_cnt ); XX aptr = (ptr) &intlist[0]; XX break; XX case 'N': XX case 'D': XX case 'O': XX case 'X': XX longlist = NEW( long, list_cnt ); XX aptr = (ptr) &longlist[0]; XX break; XX case 'f': XX fltlist = NEW( float, list_cnt ); XX aptr = (ptr) &fltlist[0]; XX break; XX case 'F': XX dbllist = NEW( double, list_cnt ); XX aptr = (ptr) &dbllist[0]; XX break; XX } XX else XX switch( typchr ) XX { XX case 's': XX strlist = RENEW( char *, strlist, XX list_cnt ); XX aptr = (ptr) &strlist[list_cnt-1]; XX break; XX case 'n': XX case 'd': XX case 'o': XX case 'x': XX intlist = RENEW( int, intlist, XX list_cnt ); XX aptr = (ptr) &intlist[list_cnt-1]; XX break; XX case 'N': XX case 'D': XX case 'O': XX case 'X': XX longlist = RENEW( long, longlist, XX list_cnt ); XX aptr = (ptr) &longlist[list_cnt-1]; XX break; XX case 'f': XX fltlist = RENEW( float, fltlist, XX list_cnt ); XX aptr = (ptr) &fltlist[list_cnt-1]; XX break; XX case 'F': XX dbllist = RENEW( double, dbllist, XX list_cnt ); XX aptr = (ptr) &dbllist[list_cnt-1]; XX break; XX } XX } XX else XX aptr = va_arg( argl, ptr ); XX if ( typchr == 's' ) XX { XX if ( ! comma_list ) XX *(char **)aptr = ncp; XX } XX else XX { XX nopt = 0; XX do { XX /* XX * Need to update aptr if parsing XX * a comma list XX */ XX if ( comma_list && nopt > 0 ) XX { XX ncp = strlist[nopt]; XX switch( typchr ) XX { XX case 'n': XX case 'd': XX case 'o': XX case 'x': XX aptr = (ptr) &intlist[nopt]; XX break; XX case 'N': XX case 'D': XX case 'O': XX case 'X': XX aptr = (ptr) &longlist[nopt]; XX break; XX case 'f': XX aptr = (ptr) &fltlist[nopt]; XX break; XX case 'F': XX aptr = (ptr) &dbllist[nopt]; XX break; XX } XX } XX /* XX * Do conversion for n and N types XX */ XX tmpflg = typchr; XX if (typchr == 'n' || typchr == 'N' ) XX if (*ncp != '0') XX tmpflg = 'd'; XX else if (*(ncp+1) == 'x' || XX *(ncp+1) == 'X') XX { XX tmpflg = 'x'; XX ncp += 2; XX } XX else XX tmpflg = 'o'; XX if (typchr == 'N') XX toupper( tmpflg ); XX#ifndef QUICK XX cntrl[1] = tmpflg;/* put in conversion */ XX if (sscanf (ncp, cntrl, aptr, junk) != 1) XX ERROR (Bad numeric argument); XX#else XX if (numcvt(ncp, tmpflg, aptr) != 1) XX ERROR (Bad numeric argument); XX#endif XX } while ( comma_list && ++nopt < list_cnt ); XX } XX check += cnt; XX arg_used[cnt] = 1; XX required = NO; XX /* XX * If not looking for multiple args, XX * then done, otherwise, keep looking. XX */ XX if ( !( list_of && !comma_list ) ) XX break; XX else XX continue; XX } XX if (required) XX switch (typchr) XX { XX case 'x': XX case 'X': XX ERROR (missing hexadecimal argument); XX case 's': XX ERROR (missing string argument); XX case 'o': XX case 'O': XX ERROR (missing octal argument); XX case 'd': XX case 'D': XX ERROR (missing decimal argument); XX case 'f': XX case 'F': XX ERROR (missing floating argument); XX case 'n': XX case 'N': XX ERROR (missing numeric argument); XX } XX if ( list_cnt > 0 ) XX { XX *cnt_arg = list_cnt; XX switch ( typchr ) XX { XX case 's': XX *va_arg( argl, char *** ) = strlist; XX break; XX case 'n': XX case 'd': XX case 'o': XX case 'x': XX *va_arg( argl, int ** ) = intlist; XX break; XX case 'N': XX case 'D': XX case 'O': XX case 'X': XX *va_arg( argl, long ** ) = longlist; XX break; XX case 'f': XX *va_arg( argl, float ** ) = fltlist; XX break; XX case 'F': XX *va_arg( argl, double **) = dbllist; XX break; XX } XX if ( typchr != 's' ) XX free( (char *) strlist ); XX } XX else if ( cnt >= argc ) XX { XX /* Fell off end looking, so must eat the arg */ XX if ( typchr == 's' ) XX va_arg( argl, char * ); XX else XX va_arg( argl, ptr ); XX } XX break; XX default: /* error */ XX fprintf (stderr, "error in call to scanargs\n"); XX return (0); XX } XX } XX } XX /* Count up empty flags */ XX for (cnt=1; cnt argv[0] && *cp != '/'; cp-- ) XX ; /* find the last / */ XX if ( *cp == '/' ) XX cp++; XX fprintf( stderr, "%s", cp ); XX cp = format + 1; /* reset to where it should be */ XX } XX while (putc (*cp++, stderr) != ' '); XX } XX else XX fprintf (stderr, "?? "); XX while (*cp == ' ') XX cp++; XX prformat (cp, NO); XX free(arg_used); XX return 0; XX} XXstatic char * XXprformat (format, recurse) XXchar *format; XX{ XX register char *cp; XX bool required, comma_list; XX int list_of; XX cp = format; XX if (recurse) XX putc (' ', stderr); XX required = NO; XX list_of = 0; XX comma_list = NO; XX while (*cp) XX { XX switch (*cp) XX { XX default: XX cp++; XX break; XX case ' ': XX case '\t': XX case '\n': XX putc(*cp, stderr); XX format = ++cp; XX break; XX case '!': XX required = YES; XX case '%': XXreswitch: XX switch (*++cp) XX { XX case ',': XX comma_list++; XX case '*': XX list_of++; XX goto reswitch; XX case '$': /* "rest" of argument list */ XX if (!required) XX putc ('[', stderr); XX for (; format < cp - 1 - list_of; format++) XX putc (*format, stderr); XX fputs( " ...", stderr ); XX if ( !required ) XX putc( ']', stderr ); XX break; XX case '-': /* flags */ XX if (!required) XX putc ('[', stderr); XX putc ('-', stderr); XX if (cp - format > 2 + list_of) XX putc ('{', stderr); XX cp = format; XX while (*cp != '%' && *cp != '!') XX putc (*cp++, stderr); XX if (cp - format > 1 + list_of) XX putc ('}', stderr); XX cp += 2; /* skip !- or %- */ XX if (*cp && !isspace(*cp)) XX cp = prformat (cp, YES); XX /* this is a recursive call */ XX if (!required) XX putc (']', stderr); XX break; XX case 's': /* char string */ XX case 'd': /* decimal # */ XX case 'o': /* octal # */ XX case 'x': /* hexadecimal # */ XX case 'f': /* floating # */ XX case 'D': /* long decimal # */ XX case 'O': /* long octal # */ XX case 'X': /* long hexadecimal # */ XX case 'F': /* double precision floating # */ XX case 'n': /* numeric arg (C format) */ XX case 'N': /* long numeric arg */ XX if (!required) XX putc ('[', stderr); XX for (; format < cp - 1 - list_of; format++) XX putc (*format, stderr); XX if ( list_of != 0 ) XX { XX if ( comma_list ) XX putc( ',', stderr ); XX else XX putc( ' ', stderr ); XX fputs( "...", stderr ); XX } XX if (!required) XX putc (']', stderr); XX break; XX default: XX break; XX } XX required = NO; XX list_of = NO; XX comma_list = NO; XX if (*cp) /* check for end of string */ XX format = ++cp; XX if (*cp && !isspace(*cp)) XX putc (' ', stderr); XX } XX if (recurse && isspace(*cp)) XX break; XX } XX if (!recurse) XX putc ('\n', stderr); XX return (cp); XX} XX/* XX * isnum - determine whether a string MIGHT represent a number. XX * typchr indicates the type of argument we are looking for, and XX * determines the legal character set. If comma_list is YES, then XX * space and comma are also legal characters. XX */ XXstatic bool XXisnum( str, typchr, comma_list ) XXregister char * str; XXchar typchr; XXbool comma_list; XX{ XX register char * allowed, * digits, * cp; XX bool hasdigit = NO; XX switch( typchr ) XX { XX case 'n': XX case 'N': XX allowed = " \t,+-x0123456789abcdefABCDEF"; XX break; XX case 'd': XX case 'D': XX allowed = " \t,+-0123456789"; XX break; XX case 'o': XX case 'O': XX allowed = " \t,01234567"; XX break; XX case 'x': XX case 'X': XX allowed = " \t,0123456789abcdefABCDEF"; XX break; XX case 'f': XX case 'F': XX allowed = " \t,+-eE.0123456789"; XX break; XX case 's': /* only throw out decimal numbers */ XX default: XX allowed = " \t,+-.0123456789"; XX break; XX } XX digits = allowed; XX while ( *digits != '0' ) XX digits++; XX if ( ! comma_list ) XX allowed += 3; /* then don't allow space, tab, comma */ XX while ( *str != '\0' ) XX { XX for ( cp = allowed; *cp != '\0' && *cp != *str; cp++ ) XX ; XX if ( *cp == '\0' ) XX return NO; /* if not in allowed chars, not number */ XX if ( cp - digits >= 0 ) XX hasdigit = YES; XX str++; XX } XX return hasdigit; XX} XX#ifdef QUICK XXnumcvt(str, conv, val) XXregister char *str; XXchar conv; XXint *val; XX{ XX int base, neg = 0; XX register unsigned int d; XX long retval = 0; XX register char *digits; XX extern char *index(); XX if (conv == 'o' || conv == 'O') XX base = 8; XX else if (conv == 'd' || conv == 'D') XX base = 10; XX else if (conv == 'x' || conv == 'X') XX base = 16; XX else XX return 0; XX if (*str == '-') XX { XX neg = 1; XX str++; XX } XX while (*str) XX { XX if (*str >= '0' && *str < '0'+base) XX d = *str - '0'; XX else if (base == 16 && *str >= 'a' && *str <= 'f') XX d = 10 + *str - 'a'; XX else if (base == 16 && *str >= 'A' && *str <= 'F') XX d = 10 + *str - 'A'; XX else XX return 0; XX retval = retval*base + d; XX str++; XX } XX if (neg) XX retval = -retval; XX if (conv == 'D' || conv == 'O' || conv == 'X') XX *(long *) val = retval; XX else XX *val = (int) retval; XX return 1; XX} XX#endif QUICK @//E*O*F scanargs.c// chmod u=rw,g=rw,o=rw scanargs.c echo Inspecting for damage in transit... temp=/tmp/sharin$$; dtemp=/tmp/sharout$$ trap "rm -f $temp $dtemp; exit" 0 1 2 3 15 cat > $temp <<\!!! 29 244 1409 README 15 73 436 Makefile 96 593 3593 newscnt.1 437 1727 11200 newscnt.c 833 3103 19707 scanargs.c 1410 5740 36345 total !!! wc README Makefile newscnt.1 newscnt.c scanargs.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp if test -s $dtemp then echo "Ouch [diff of wc output]:" ; cat $dtemp else echo "No problems found." fi exit 0