limits

Moderator, John Quarterman std-unix at ut-sally.UUCP
Wed Nov 20 09:38:14 AEST 1985


[ One thing to remember in this discussion is that standards committees
are loath to attempt to standardize and innovate at the same time.
In this particular posting (as in any others), note that parenthetical
remarks in square brackets are those of the moderator only if they end
with -mod ]

Date: Tue, 19 Nov 85 10:00:44 cst
From: allegra!jpl

> Date: Sat, 16 Nov 85 15:11:46 est
> From: seismo!hadron!jsdy (Joseph S. D. Yao)
> 
> Reading in limits on a per-machine basis is a good idea.
> However, the idea of reading in a struct or using a numerical
> ID for limits both constrain expandability.  I also have
> problems with reading directly from limits.h.  Let's do it
> from a file like /etc/limits.

Let's kick it around a little first.  I agree with your decision to
keep this out of the kernel, but I'll amplify to avoid misunderstandings.

1)  I want a general mechanism that applies to all include
    files, not just limits.h.

2)  The kernel is a lousy place to store the limits.h information.
    It is preposterous to have a separate system call for each
    value.  If you return a structure that includes all the values,
    you trash existing binaries whenever you make the returned
    structure larger by adding another value.  If you use an index
    to return a single value, you can only port to systems that
    agree with you on the index values.

3)  It is unworkable.  If I want to run my code on a kernel where
    the maintainers (wisely) refused to clutter up their system,
    not only can I not port my binaries, I can't even port my
    source code.

KEEP THIS AT USER LEVEL WHERE IT BELONGS! AND WHERE WE CAN CONTROL IT!!!
[Pardon me, I seem to be getting carried away in a moderated newsgroup.]
[ Now that you mention it....  -mod ]

My major gripe with Joseph's solution is point 1) above [although his
program can be made to serve for other include files] together with a
hard-earned awareness that duplicating information from limit.s in a
file like /etc/limits WITHOUT ENFORCING THE CONSISTENCY OF THAT
INFORMATION is a sure recipe for disaster.  So let's try to address the
problems with reading directly from limits.h.  To keep the discussion
concrete, I have a first-cut at such a program at the end of this
diatribe.  Type lines like
    <stdio.h> _NFILE NULL frobaz
at it, and it will tell you what the values of _NFILE, NULL and frobaz
are according to include file <stdio.h>.  If you actually run the
program, you will recognize one problem.  It's slow.  I claim this is
a non-problem: look up your values once, and then run nice and fast.
If you READ the program (unencumbered as it is by comments), you will
discover more serious problems.

1)  The program uses ``cc -E'' and ``sed'' to extract the information.
    I can't glibly laugh this one off.  If you don't have cc (the sed
    part could be built into the program) on the target machine, you
    can't look up the values.  I see this as by far the most serious
    problem, even though all the machines I use have cc.

2)  It relies on unpromised characteristics of the C preprocessor.
    The value to be looked up gets sandwiched between ``=== '' and
    `` ===\n'', and sed expects to find the value on a line of this
    form.  The preprocessor is free to tamper with the format of
    the line, although, on all the machines I have tried it on, it
    leaves the format alone.  This is not such a big deal.  In a
    practical implementation, the action of sed should probably be
    replaced by more robust [and efficient] code in the lookup
    routine.  Another alternative would be to generate a real
    C program, compile it and execute it to generate the results.
    This would eliminate any peculiarities of the preprocessor,
    but it would mean that the include file be syntactically
    complete.  For example, you couldn't have any undefined externs,
    or undeclared types.  [I have railed elsewhere on the value of
    of making all #include files idempotent, and then always including
    everything you need.  I'll spare you a playback.]

3)  It only applies to #defined constants.  Another non-problem.
    Variables should be initialized using #defined constants anyway,
    so there should be a #defined constant around to use.

4)  The runtime environment is lacking all sorts of -D and -I
    arguments and the working directory that may make a difference
    at compile time.  Two answers: if they REALLY make a difference,
    neither a kernel-based nor a fixed table-based solution will do
    any better.  In fact, it would be real nice if this information
    were available in symbolic form at compile time, so one could
    drop the information in the object code, as one does with SCCS
    or RCS information.  It would often be invaluable to be able to
    snuffle through a binary and determine what compile-time options
    were used to produce it.  If that were possible (I won't hold my
    breath), the lookup routine could more nearly duplicate what
    happened at compile time, and address the gripe about the lack
    of #if's.

Enough from me for now.  The promised code follows.

John P. Linderman  Better Living through Portability  allegra!jpl

#include <stdio.h>

static char **
mkvect(file, lines)
    char *file;
    int lines;
{
    FILE *iop;
    char **pp, *p;
    char *malloc();
    char **retval = (char **) NULL;
    int c;
    int chars = 0;
    int vects = 1;

    if ((lines > 0) && ((iop = fopen(file, "r")) != NULL)) {
	while ((c = getc(iop)) != EOF) {
	    chars++;
	    if (c == '\n') vects++;
	}
	if ((ferror(iop) == 0) && (lines == vects)) {
	    retval = (char **) malloc((vects * sizeof(char *)) + chars);
	    if (retval != (char **) NULL) {
		(void) clearerr(iop);
		(void) rewind(iop);
		p = (char *)(retval + vects);
		pp = retval;
		*pp++ = p;
		do {
		    if ((c = getc(iop)) == EOF) {
			(void) free((char *) retval);
			retval = (char **) NULL;
			break;
		    }
		    if (c == '\n') {
			*p++ = '\0';
			*pp++ = p;
		    } else *p++ = c;
		} while (--chars);
		if (c != EOF) *--pp = (char *) NULL;
	    }
	}
	(void) fclose(iop);
    }
    return (retval);
}


char **
rtlook(inc, val)
    char *inc;
    char **val;
{
#define NAMEZ 12
    static char tmplate[NAMEZ] = "/tmp/XXXXXX";
    char ifile[NAMEZ];
    char ofile[NAMEZ];
    char commnd[512];
    char *strcpy(), *mktemp();
    char **retval = (char **) NULL;
    int n = 1;
    FILE *iop, *popen();

    (void) strcpy(ifile, tmplate);
    (void) mktemp(ifile);
    if ((iop = fopen(ifile, "w")) != NULL) {
	(void) strcpy(ofile, tmplate);
	(void) mktemp(ofile);
	(void) fprintf(iop, "#include %s\n", inc);
	for (; (*val != NULL) && (**val != '\0'); n++, val++) {
	    (void) fprintf(iop, "\n#ifdef %s\n", *val);
	    (void) fprintf(iop, "=== %s ===\n", *val);
	    (void) fprintf(iop, "#else\n");
	    (void) fprintf(iop, "===  ===\n");
	    (void) fprintf(iop, "#endif\n");
	}
	if ((fflush(iop) != EOF) && !ferror(iop) &&
	    (fclose(iop) != EOF)) {
	    (void) sprintf(commnd,
		    "cc -E %s | sed -n '/^=== \\(.*\\) ===$/s//\\1/p' > %s",
		    ifile, ofile);
	    (void) system(commnd);
	    retval = mkvect(ofile, n);
	    (void) unlink(ofile);
	}
 	(void) unlink(ifile);
    }
    return (retval);
}


#include <ctype.h>

main()
{
    char line[512];
    char *p, **qq, **pp, *q[300];
    int n;

    while (gets(line) != NULL) {
	for (p = line, pp = q; *p;) {
	    while (isspace(*p)) *p++ = '\0';
	    if (*p) {
		*pp++ = p;
		do p++; while(*p && !isspace(*p));
	    }
	}
	*pp = NULL;
	if (pp != q) {
	    if ((qq = rtlook(q[0], q+1)) == NULL) {
		(void) printf("lookup failed\n");
	    } else {
		n = 0;
		for (pp = q+1; (*pp != (char *) NULL) && **pp; pp++, n++) {
		    (void) printf("%s => %s\n", *pp, qq[n]);
		}
		(void) free((char *) qq);
	    }
	}
    }
}

Volume-Number: Volume 3, Number 27



More information about the Mod.std.unix mailing list