[mod.std.unix] limits.h

std-unix@ut-sally.UUCP (Moderator, John Quarterman) (11/16/85)

Date: Fri, 15 Nov 85 00:48:36 cst
From: allegra!jpl (John P. Linderman)

I agree that values like OPEN_MAX that may well vary among binary
compatible machines should be determinable at execution time.  It
makes a lot of sense to have a routine that would look them up in
the copy of limits.h on the runtime machine.  If the file were
missing (or otherwise unusable), default values could be compiled
in from the copy of limits.h on the compile-time machine.  This
would leave us with sensible defaults, even on a bare-bones machine,
and a high level of compatibility across binary compatible machines,
regardless of the machine a source was compiled on.

I don't imagine people will bury these lookups deep in nested loops,
so efficiency shouldn't be much of an issue.  A lookup that invoked
cpp would be easy to write and would guarantee compatibility with
compile-time lookups.

John P. Linderman  allegra!jpl

Volume-Number: Volume 3, Number 20

std-unix@ut-sally.UUCP (Moderator, John Quarterman) (11/17/85)

[ Everybody talks about it, but this one did something about it.  -mod ]

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.

By the way -- one person said that define'd limits were needed
for #if expressions in code.  What about if () expressions?  The
only case in which the former couldn't be handled by the latter is
in the case that you are really squeezed for core.  ("main memory"
to all you young whippersnappers.)

/* #include <local.h> */

/*********************************************************************\
**
** limits -- read limits from the limits file.
**
** Copyright and disclaimers:
**	This code is Copyright (c) 1985 by one or more of:
**		The author named below;
**		The author's institution named below;
**		(No institution paid for this code, so that's it).
**	Permission is hereby granted to use or copy this code, in
**	whole or in part, only under the conditions that this copyright
**	notice (including other portions of this header comment and
**	code herein referenced) are retained with all parts of this
**	code; that all changes or corrections to this code be fully
**	annotated in the code and notification of such be sent to
**	the author and authors institution; and that this code not
**	specifically be used for profit (although it may be included
**	to enhance a larger work which would otherwise have still
**	been used for profit).
**
** Syntax:
**	setlimits(filename)
**	  char *filename;
**	getlimit(name, buf, length)
**	  char *name;	int buf[], length;
**	endlimits()
**
** Description:
**	This is an implementation of the suggested code to read the
**	per-machine limits from a limits file.  It assumes a file of
**	the format:
**		limit-name	limit-value[, limit_value ... ]
**	e.g.:
**		char_bit	8
**		char_min	-128
**		char_max	128
**	etc.  Lines that start with '#' are assumed to be comment
**	lines.  Lines that start with white space are assumed to
**	be continuation lines.
**
**	Setlimits() returns SUCCESS if the file whose name is passed
**	is readable, FAILURE if not.  On a SUCCESS return, the file is
**	set but not opened.  A NULL argument returns to the original
**	file name.  Getlimit() returns FAILURE if no value, otherwise
**	the number of values available.  (May be > length.)
**	Endlimits() closes the limits file, if open.  No value is
**	returned.
**
#ifdef	SCCS
** Last modifed %G% %U%.  Last retrieved %H% %T%.
#endif	SCCS
**
** Author:
**	Joseph S. D. Yao
**	Engineering and Information Sciences Division
**	Hadron, Inc.
**	9990 Lee Highway
**	Fairfax VA   22030
**	(703) 359-6163
**
** Routines:
**	int setlimits(filename)
**	int getlimit(name, buf, length)
**	void endlimits()
**	static int getnum(str)
**
** Declared:
**	static char limfile[] = "/etc/limits"
**	static char *curlimfile = limfile
**	static FILE *limf = (FILE *) NULL
**
\*********************************************************************/

#ifndef	lint
# ifdef SCCS
  static char SCCS_id[] = "%W%";
#  else
  static char RCS_id[] =
	"@(#)$Header:$";
# endif	SCCS
#endif	lint

#include <stdio.h>
#include <ctype.h>
#ifdef	limits
# include <limits.h>
#endif	limits
#include <sys/types.h>

/* #include "std.h" */
#define TRUE	1
#define FALSE	0

#define SUCCESS	0
#define FAILURE	(-1)

#define NUL	'\0'

#define STDIN	0
#define STDOUT	1
#define STDERR	2

#define ROPEN	0
#define WOPEN	1
#define RWOPEN	2
#define XOPEN	3
#define RXOPEN	4
#define WXOPEN	5
#define RWXOPEN	6

#define READ	"r"
#define WRITE	"w"
#define APPEND	"a"
#define WREAD	"r+"
#define RWRITE	"w+"
#define RAPPEND	"a+"

#define F_ACC	00
#define X_ACC	01
#define W_ACC	02
#define R_ACC	04

#define ever		(;;)
#define until(x)	while (!(x))
#define unless(x)	if (!(x))

#define streq(a,b)	(strcmp(a, b) == 0)
#define strneq(a,b,n)	(strncmp(a, b, n) == 0)

typedef char bool;	/* Smallest data object */
typedef int boolean;	/* Function argument and return. */
/**/

#define NL	'\n'
#define COMMENT	'#'
#define COMMA	','
#define PLUS	'+'
#define MINUS	'-'

static char limfile[] = "/etc/limits";
static char *curlimfile = limfile;

static FILE *limf = (FILE *) NULL;

/*
** Setlimits() returns SUCCESS if the file whose name is passed
** is readable, FAILURE if not.  On a SUCCESS return, the file is
** set but not opened.  A NULL argument returns to the original
** file name, whether or not the original file is readable.
** Note that a non-NULL filename must not disappear (e.g., be
** reclaimed on the stack, free'd, or written over) before it
** is opened in getlimit()!
*/
int setlimits(filename)
  char *filename;	/* NULL or the name of the new limits file. */
{
	void endlimits();

	/* If name is NULL, go to the original. */
	if (filename == (char *) NULL) {
		if (limf != NULL)
			endlimits();
		curlimfile = limfile;
		return(SUCCESS);
	}

	/* If new file can't be read, forget it. */
	if (access(filename, R_ACC) < 0)
		return(FAILURE);

	/* Close old open file. */
	if (limf != NULL)
		endlimits();
	/* And use new one. */
	curlimfile = filename;
	return(SUCCESS);
}

/*
** Getlimit() returns FAILURE if no value, otherwise the number
** of values available.  The number of values may be > 'length';
** but the number stored into buf[] will never be.
** Note that the result is always returned as a number of integers.
** It is up to the calling program to put them together into longs,
** complexes, etc. or decompose them into chars, bitfields, or
** whatever.
**
** The search starts from wherever it left off, which improves
** performance if names are called in order from the file.
** (Say, alphabetical order.)
*/
int getlimit(name, buf, length)
  char *name;	/* Name of the limit wanted. */
  int buf[];	/* Pointer to the array of int's for these values. */
  int length;
{
	register char *cp;
	register int n;
	bool been_here;
	bool new_line;
	char inbuf[BUFSIZ];
	off_t current;
	extern char *index();

	/* If not open, open it. */
	if (limf == (FILE *) NULL)
		limf = fopen(curlimfile, READ);

	/* If still not open, kick it in. */
	if (limf == (FILE *) NULL)
		return(FAILURE);

	n = strlen(name);
	/* Find where we currently are in the file. */
	current = ftell(limf);
	/* Initialise boolean values. */
	new_line = TRUE;
	been_here = FALSE;

	/* Search, starting wherever we left off. */
	for ever {
		/* Get a line from the file */
		cp = fgets(inbuf, sizeof(inbuf), limf);
		if (cp != (char *) NULL) {

			/* If gotten, test: if start of line */
			if (new_line &&
			    /* and not a comment line */
			    *cp != COMMENT &&
			    /* and not a continuation line */
			    !isspace(*cp) &&
			    /* and the limit name is first */
			    strneq(cp, name, n) &&
			    /* followed by white space */
			    isspace(cp[n]))
				break;	/* go handle it. */

			/* Check whether a NL terminated the read. */
			new_line = (index(cp, NL) != (char *) NULL);
		} else {
			/* If not gotten, re-cycle. */

			/*
			** Make sure the file doesn't get changed
			** and cause an infinite loop here.
			*/
			if (been_here)
				return(FAILURE);
			been_here = TRUE;

			/* Go back. */
			(void) fseek(limf, (off_t) 0L, 0);
			/* Well this starts a new line, no? */
			new_line = TRUE;
		}

		/* If we're where we started, we failed. */
		if (current == ftell(limf))
			return(FAILURE);
	}

	/*
	** We only get here if we have a line in inbuf (or the start
	** of one) that contains the limit name.  Skip over it and
	** start passing values.
	*/
	cp += n + 1;
	n = 0;
	/* Check whether a NL terminated the read. */
	new_line = (index(cp, NL) != (char *) NULL);

	/*
	** Our value may be preceded by whitespace, and is ended
	** by a comma or whitespace or comment or EOL.
	*/
	for ever {
		/* Skip any initial white space. */
		while (isspace(*cp))
			++cp;

		/* Check whether line is done. */
		if (*cp == COMMENT || *cp == NUL) {
			/* Save current position */
			current = ftell(limf);
			/* Get new line. */
			cp = fgets(inbuf, sizeof(inbuf), limf);
			/* If EOF, do it over again at loc 0. */
			if (cp == (char *) NULL) {
				(void) fseek(limf, (off_t) 0L, 0);
				current = (off_t) 0L;
				cp = fgets(inbuf, sizeof(inbuf), limf);
				/* There has to be data here. */
				if (cp == (char *) NULL) {
					/* "never happen" */
					break;
				}
			}

			/*
			** If new line is start of line but no white
			** space, go back to start of line and return.
			*/
			if (!new_line || !isspace(*cp)) {
				(void) fseek(limf, current, 0);
				break;
			}

			/* Check whether a NL terminated the read. */
			new_line = (index(cp, NL) != (char *) NULL);

			continue;
		}

		/* If it can fit, store the present value. */
		if (n < length)
			buf[n] = getnum(cp);

		/* Count another value. */
		++n;

		/*
		** We want to break but not skip on space, COMMENT, and
		** NUL.  We want to skip and break on COMMA.  We want
		** just to skip over everything else.
		*/
		while (!isspace(*cp) && *cp != COMMENT && *cp != NUL &&
		       *cp++ != COMMA);
	}

	/* Return the number of values found. */
	return(n);
}

/*
** Endlimits() closes the limits file, if open.  No value is
** returned.
*/
void endlimits()
{
	if (limf != (FILE *) NULL) {
		(void) fclose(limf);
		limf = (FILE *) NULL;
	}
}

/*
** This routine converts strings to numbers of base specified
** by 0x or 0 or no prefix.  Signs are allowed.  Atoi() isn't
** used because it doesn't allow different bases, and for the
** sake of consistency.
*/
static int getnum(str)
  register char *str;
{
	register int base = 10;
	register int i = 0, digit;
	register int sign = 1;

	while (isspace(*str) || *str == PLUS || *str == MINUS) {
		if (*str == MINUS)
			sign = -sign;
		++str;
	}

	if (*str == '0') {
		++str;
		if (*str == 'x') {
			++str;
			base = 16;
		} else
			base = 8;
	}

	/*
	** As opposed to code that assumes contiguous digits and
	** letters, the following will work even for (*shudder*)
	** EBCDIC.  Heck, it'll work for TTS and Baudot!
	*/
	for ever {
		digit = -1;
		switch (*str++) {
		  case '0':	digit = 0; break;
		  case '1':	digit = 1; break;
		  case '2':	digit = 2; break;
		  case '3':	digit = 3; break;
		  case '4':	digit = 4; break;
		  case '5':	digit = 5; break;
		  case '6':	digit = 6; break;
		  case '7':	digit = 7; break;

		  case '8':	if (base > 8) digit = 8; break;
		  case '9':	if (base > 9) digit = 9; break;

		  case 'A':
		  case 'a':
			if (base > 10)
				digit = 10;
			break;

		  case 'B':
		  case 'b':
			if (base > 11)
				digit = 11;
			break;

		  case 'C':
		  case 'c':
			if (base > 12)
				digit = 12;
			break;

		  case 'D':
		  case 'd':
			if (base > 13)
				digit = 13;
			break;

		  case 'E':
		  case 'e':
			if (base > 14)
				digit = 14;
			break;

		  case 'F':
		  case 'f':
			if (base > 15)
				digit = 15;
			break;

		  default:	break;
		}

		if (digit < 0)
			break;

		i *= base;
		i += digit;
	}

	if (sign < 0)
		return(-i);
	return(i);
}

	Joe Yao		hadron!jsdy@seismo.{CSS.GOV,ARPA,UUCP}

Volume-Number: Volume 3, Number 24