/*
 * Copyright (c) 1995 Sun Microsystems, Inc.
 * All rights reserved.
 * 
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 * 
 * IN NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
 * OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF SUN
 * MICROSYSTEMS, INC. HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * SUN MICROSYSTEMS, INC. SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THE SOFTWARE PROVIDED
 * HEREUNDER IS ON AN "AS IS" BASIS, AND SUN MICROSYSTEMS, INC. HAS NO
 * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
 * MODIFICATIONS.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pwd.h>
#include <sys/systeminfo.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stropts.h>
#include <poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <tcl.h>
#include "doomarena.h"

int
SysError(Tcl_Interp *interp, char *command, char *errstr)
{
  char buf[1024];

  sprintf(buf, "\"%s %s\" : %s",
	  command, errstr,
	  errno ? (char *)strerror(errno) : "name not defined on this system");
  Tcl_SetResult(interp, buf, TCL_VOLATILE);
  return TCL_ERROR;
}

char *addr_type_table[] = {
  "AF_UNSPEC",
  "AF_UNIX",
  "AF_INET",
  "AF_IMPLINK",
  "AF_PUP",
  "AF_CHAOS",
  "AF_NS",
  "AF_NBS",
  "AF_ECMA",
  "AF_DATAKIT",
  "AF_CCITT",
  "AF_SNA",
  "AF_DECnet",
  "AF_DLI",
  "AF_LAT",
  "AF_HYLINK",
  "AF_APPLETALK",
  "AF_NIT",
  "AF_802",
  "AF_OSI",
  "AF_X25",
  "AF_OSINET",
  "AF_GOSIP",
  "AF_IPX",
  NULL
};

int
GetHostCmd(ClientData clientData, Tcl_Interp *interp,
	   int argc, char **argv)
{
  struct hostent ent;
  struct in_addr in;
  char buf[4096];
  char ibuf[32];
  int status, i, type;
  u_long addr;
  char **aliases, **addr_list, **addr_type;
  Tcl_DString dstr;

  if ((int)clientData == BY_NAME) {
    if (argc != 2) {
      sprintf(interp->result, "wrong # args: must be \"%s name\"",
	      argv[0]);
      return TCL_ERROR;
    }
    if (!gethostbyname_r(argv[1], &ent, buf, 4096, &status)) {
      errno = status;
      return SysError(interp, argv[0], argv[1]);
    }
  }
  else {
    if (argc != 2 && argc != 3) {
      sprintf(interp->result, "wrong # args: must be \"%s addr ?type?\"",
	      argv[0]);
      return TCL_ERROR;
    }

    if (argc == 3) {
      i = 0;
      type = -1;
      addr_type = addr_type_table;
      while (*addr_type) {
	if (!strcmp(addr_type, argv[2])) {
	  type = i;
	  break;
	}
	i++;
      }
      if (type == -1) {
	Tcl_AppendResult(interp,
			 "invalid address type \"",
			 argv[2],
			 "\": should be one of",
			 NULL);
	addr_type = addr_type_table;
	while (*addr_type) {
	  Tcl_AppendResult(interp, " ", *addr_type, NULL);
	  addr_type++;
	}
	return TCL_ERROR;
      }
    }
    else {
      type = AF_INET;
    }

    if ((int)(addr = inet_addr(argv[1])) == -1) {
      sprintf(interp->result, "IP-address must be of the form a.b.c.d");
      return TCL_ERROR;
    }
    if (!gethostbyaddr_r((char *)&addr, sizeof(addr), type,
			 &ent, buf, 4096, &status)) {
      errno = status;
      return SysError(interp, argv[0], argv[1]);
    }
  }

  Tcl_DStringInit(&dstr);

  Tcl_DStringStartSublist(&dstr);
  Tcl_DStringAppendElement(&dstr, "name");
  Tcl_DStringAppendElement(&dstr, ent.h_name);
  Tcl_DStringEndSublist(&dstr);

  Tcl_DStringStartSublist(&dstr);
  Tcl_DStringAppendElement(&dstr, "aliases");
  Tcl_DStringStartSublist(&dstr);
  aliases = ent.h_aliases;
  while (*aliases) {
    Tcl_DStringAppendElement(&dstr, *aliases);
    aliases++;
  }
  Tcl_DStringEndSublist(&dstr);
  Tcl_DStringEndSublist(&dstr);

  Tcl_DStringStartSublist(&dstr);
  Tcl_DStringAppendElement(&dstr, "addr");
  Tcl_DStringStartSublist(&dstr);
  addr_list = ent.h_addr_list;
  while (*addr_list) {
    memcpy(&in.s_addr, *addr_list, sizeof(in.s_addr));
    Tcl_DStringAppendElement(&dstr, inet_ntoa(in));
    addr_list++;
  }
  Tcl_DStringEndSublist(&dstr);
  Tcl_DStringEndSublist(&dstr);

  Tcl_DStringStartSublist(&dstr);
  Tcl_DStringAppendElement(&dstr, "addrtype");
  sprintf(ibuf, "%d", ent.h_addrtype);
  if (ent.h_addrtype >= 0 && ent.h_addrtype <= AF_MAX)
    Tcl_DStringAppendElement(&dstr, addr_type_table[ent.h_addrtype]);
  else
    Tcl_DStringAppendElement(&dstr, "unknown");
  Tcl_DStringEndSublist(&dstr);

  Tcl_SetResult(interp, Tcl_DStringValue(&dstr), TCL_VOLATILE);
  Tcl_DStringFree(&dstr);
  return TCL_OK;
}

#define READ_FLAG 1
#define WRITE_FLAG 2

int
PollCmd(ClientData clientData, Tcl_Interp *interp,
      int argc, char **argv)
{
    static char *syntax= "?-read file file ...? ?-write file file ...?";
    struct pollfd *fds;
    unsigned long nfds, arg, arg_flag= 0;
    FILE *file;
    Tcl_DString result;
    int rc;

    if (argc <= 1)  {
      Tcl_AppendResult( interp, "wrong # args; ", argv[0], " ", syntax, NULL);
      return TCL_ERROR;
    }

    fds= (struct pollfd *) malloc( sizeof(struct pollfd) * argc);
    nfds= 0;

    for ( arg= 1; arg < argc; arg++)  {
	if ( strcmp( argv[arg], "-read") == 0)
	    arg_flag= READ_FLAG;
	else if ( strcmp( argv[arg], "-write") == 0)
	    arg_flag= WRITE_FLAG;
	else if ( arg_flag == 0)  {
	    Tcl_AppendResult( interp, "unknown option \"", argv[arg], "\"; ",
		argv[0], " ", syntax, NULL);
	    goto Error;
	}
	else  {
	    if ( Tcl_GetOpenFile(interp,argv[arg],0,0,&file) == TCL_ERROR) {
		Tcl_AppendResult(interp, "; poll failed", NULL);
		goto Error;
	    }
	    fds[nfds].fd= fileno( file);
	    fds[nfds].events= (arg_flag == READ_FLAG) ? POLLIN : POLLOUT;
	    nfds++;
	}
    }

    rc= poll( fds, nfds, INFTIM);
    if ( rc == -1)  {
	Tcl_AppendResult(interp, "poll error \"",
			 strerror( errno), "\"", NULL);
	goto Error;
    }

    if ( rc == 0)
      goto Ok;

    Tcl_DStringInit( &result);
    for ( arg= 0; arg < nfds; arg++)  {
	char string[80];
	if ( fds[arg].revents & POLLIN)  {
	    sprintf( string, "file%d read", fds[arg].fd);
	    Tcl_DStringAppendElement( &result, string);
	}
	if ( fds[arg].revents & POLLOUT)  {
	    sprintf( string, "file%d write", fds[arg].fd);
	    Tcl_DStringAppendElement( &result, string);
	}
	if ( fds[arg].revents & POLLERR)  {
	    sprintf( string, "file%d error", fds[arg].fd);
	    Tcl_DStringAppendElement( &result, string);
	}
	if ( fds[arg].revents & POLLHUP)  {
	    sprintf( string, "file%d hup", fds[arg].fd);
	    Tcl_DStringAppendElement( &result, string);
	}
	if ( fds[arg].revents & POLLNVAL)  {
	    sprintf( string, "file%d invalid", fds[arg].fd);
	    Tcl_DStringAppendElement( &result, string);
	}
    }
    Tcl_DStringResult( interp, &result);

  Ok:
    free(fds);
    return TCL_OK;

  Error:
    free(fds);
    return TCL_ERROR;
}

/*
 * getpwname and getpwuid
 */

int
PWCmd(ClientData clientData, Tcl_Interp *interp,
      int argc, char **argv)
{
  struct passwd passwd_struct;
  struct passwd *passwd;
  char *name;
  uid_t uid;
  char bigbuf[4096];
  char smallbuf[32];

  if (argc != 2) {
    sprintf(interp->result, "wrong # args: must be \"%s %s\"",
	    argv[0], ((int)clientData == PW_NAM) ? "name" : "uid");
    return TCL_ERROR;
  }

  if ((int)clientData == PW_NAM) {
    name = argv[1];
    passwd = getpwnam_r(argv[1], &passwd_struct, bigbuf, 4096);
  }
  else {
    if (Tcl_GetInt(interp, argv[1], (int *)&uid) != TCL_OK)
      return TCL_ERROR;
    passwd = getpwuid_r(uid, &passwd_struct, bigbuf, 4096);
  }

  if (!passwd) {
    sprintf(interp->result, "lookup failed for \"%s\"", argv[1]);
    return TCL_ERROR;
  }

  Tcl_AppendResult(interp, "{", NULL);
  Tcl_AppendElement(interp, "name");
  Tcl_AppendElement(interp, passwd->pw_name);
  Tcl_AppendResult(interp, "}", NULL);

  Tcl_AppendResult(interp, " {", NULL);
  Tcl_AppendElement(interp, "passwd");
  Tcl_AppendElement(interp, passwd->pw_passwd);
  Tcl_AppendResult(interp, "}", NULL);

  Tcl_AppendResult(interp, " {", NULL);
  Tcl_AppendElement(interp, "uid");
  sprintf(smallbuf, "%d", passwd->pw_uid);
  Tcl_AppendElement(interp, smallbuf);
  Tcl_AppendResult(interp, "}", NULL);

  Tcl_AppendResult(interp, " {", NULL);
  Tcl_AppendElement(interp, "gid");
  sprintf(smallbuf, "%d", passwd->pw_gid);
  Tcl_AppendElement(interp, smallbuf);
  Tcl_AppendResult(interp, "}", NULL);

  Tcl_AppendResult(interp, " {", NULL);
  Tcl_AppendElement(interp, "age");
  Tcl_AppendElement(interp, passwd->pw_age);
  Tcl_AppendResult(interp, "}", NULL);

  Tcl_AppendResult(interp, " {", NULL);
  Tcl_AppendElement(interp, "comment");
  Tcl_AppendElement(interp, passwd->pw_comment);
  Tcl_AppendResult(interp, "}", NULL);

  Tcl_AppendResult(interp, " {", NULL);
  Tcl_AppendElement(interp, "gecos");
  Tcl_AppendElement(interp, passwd->pw_gecos);
  Tcl_AppendResult(interp, "}", NULL);

  Tcl_AppendResult(interp, " {", NULL);
  Tcl_AppendElement(interp, "dir");
  Tcl_AppendElement(interp, passwd->pw_dir);
  Tcl_AppendResult(interp, "}", NULL);

  Tcl_AppendResult(interp, " {", NULL);
  Tcl_AppendElement(interp, "shell");
  Tcl_AppendElement(interp, passwd->pw_shell);
  Tcl_AppendResult(interp, "}", NULL);

  return TCL_OK;
}

/*
 * getrlimit and setrlimit
 */

int
RLimitCmd(ClientData clientData, Tcl_Interp *interp,
	  int argc, char **argv)
{
  int set, resource;
  rlim_t soft, hard;
  struct rlimit rlimit;
  char *errstr;
  char c1, c2;

  set = ((int)clientData == RLIMIT_SET);

  if (set) {
    if (argc != 3 && argc != 4) {
      sprintf(interp->result,
	      "wrong # args: must be \"%s resource soft_limit ?hard_limit?\"",
	      argv[0]);
      return TCL_ERROR;
    }
  }
  else {
    if (argc != 2) {
      sprintf(interp->result,
	      "wrong # args: must be \"%s resource\"",
	      argv[0]);
      return TCL_ERROR;
    }
  }

  c1 = argv[1][0];
  c2 = argv[1][1];

  if (c1 == 'c' && c2 == 'o') {
    resource = RLIMIT_CORE;
    errstr = "core";
  }
  else if (c1 == 'c' && c2 == 'p') {
    resource = RLIMIT_CPU;
    errstr = "cpu";
  }
  else if (c1 == 'd') {
    resource = RLIMIT_DATA;
    errstr = "data";
  }
  else if (c1 == 'f') {
    resource = RLIMIT_FSIZE;
    errstr = "fsize";
  }
  else if (c1 == 'n') {
    resource = RLIMIT_NOFILE;
    errstr = "nofile";
  }
  else if (c1 == 's') {
    resource = RLIMIT_STACK;
    errstr = "stack";
  }
  else if (c1 == 'v') {
    resource = RLIMIT_VMEM;
    errstr = "vmem";
  }
  else {
    sprintf(interp->result,
	    "bad option \"%s\": must be "
	    "core, cpu, data, fsize, nofile, statck, or vmem",
	    argv[1]);
    return TCL_ERROR;
  }

  if (set) {
    if (argc == 4) {
      if (Tcl_GetInt(interp, argv[3], (int *)&hard) != TCL_OK)
	return TCL_ERROR;
      rlimit.rlim_max = hard;
    }
    else {
      if (getrlimit(resource, &rlimit) == -1)
	return SysError(interp, argv[0], errstr);
    }

    if (Tcl_GetInt(interp, argv[2], (int *)&soft) != TCL_OK)
      return TCL_ERROR;
    rlimit.rlim_cur = soft;

    if (setrlimit(resource, &rlimit) == -1)
      return SysError(interp, argv[0], errstr);
  }
  else {
    if (getrlimit(resource, &rlimit) == -1)
      return SysError(interp, argv[0], errstr);
    sprintf(interp->result, "%d %d",
	    rlimit.rlim_cur, rlimit.rlim_max);
  }

  return TCL_OK;
}


/*
 * sysinfo
 */

int
SysInfoCmd(ClientData clientData, Tcl_Interp *interp,
	   int argc, char **argv)
{
  Tcl_DString dstr;
  int i;
  char buf[256];

  if (argc < 2) {
    sprintf(interp->result,
	    "wrong # args: must be \"%s option ?option ...?\"",
	    argv[0]);
    return TCL_ERROR;
  }

  Tcl_DStringInit(&dstr);

  for (i=1; i<argc; i++) {
    if (!strncmp(argv[i], "-a", 2)) {
      if (sysinfo(SI_ARCHITECTURE, buf, 256) == -1)
	return SysError(interp, argv[0], "-architecture");
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (!strncmp(argv[i], "-d", 2)) {
      if (sysinfo(SI_SRPC_DOMAIN, buf, 256) == -1)
	return SysError(interp, argv[0], "-domainname");
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (!strncmp(argv[i], "-hw_p", 5)) {
      if (sysinfo(SI_HW_PROVIDER, buf, 256) == -1)
	return SysError(interp, argv[0], "-hw_provider");
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (!strncmp(argv[i], "-hw_s", 5)) {
      if (sysinfo(SI_HW_SERIAL, buf, 256) == -1)
	return SysError(interp, argv[0], "-hw_serial");
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (!strncmp(argv[i], "-ho", 3)) {
      if (sysinfo(SI_HOSTNAME, buf, 256) == -1)
	return SysError(interp, argv[0], "-hostname");
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (!strncmp(argv[i], "-m", 2)) {
      if (sysinfo(SI_MACHINE, buf, 256) == -1)
	return SysError(interp, argv[0], "-machine");
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (!strncmp(argv[i], "-r", 2)) {
      if (sysinfo(SI_RELEASE, buf, 256) == -1)
	return SysError(interp, argv[0], "-release");
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (!strncmp(argv[i], "-sy", 3)) {
      if (sysinfo(SI_SYSNAME, buf, 256) == -1)
	return SysError(interp, argv[0], "-sysname");
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (!strncmp(argv[i], "-v", 2)) {
      if (sysinfo(SI_VERSION, buf, 256) == -1)
	return SysError(interp, argv[0], "-version");
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else {
      sprintf(interp->result,
	      "ambiguous or bad option \"%s\": must be one of "
	      "-architecture, -domainname, -hw_provider, -hw_serial, "
	      "-hostname, -machine, -release, "
	      "-sysname, or -version", argv[1]);
      Tcl_DStringFree(&dstr);
      return TCL_ERROR;
    }
  }

  Tcl_SetResult(interp, Tcl_DStringValue(&dstr), TCL_VOLATILE);
  Tcl_DStringFree(&dstr);

  return TCL_OK;
}


/*
 * sysconf
 */

int
SysConfCmd(ClientData clientData, Tcl_Interp *interp,
	   int argc, char **argv)
{
  Tcl_DString dstr;
  char c;
  int i;
  long val;
  char buf[256];

  if (argc < 2) {
    sprintf(interp->result,
	    "wrong # args: must be \"%s option ?option ...?\"",
	    argv[0]);
    return TCL_ERROR;
  }

  Tcl_DStringInit(&dstr);
  errno = 0;

  for (i=1; i<argc; i++) {
    if (argv[i][0] == '-')
      c = argv[i][1];
    else
      c = 0;

    if (c == 'a' && !strncmp(argv[i], "-ar", 3)) {
      val = sysconf(_SC_ARG_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-arg_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'c' && !strncmp(argv[i], "-ch", 3)) {
      val = sysconf(_SC_CHILD_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-child_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'c' && !strncmp(argv[i], "-cl", 3)) {
      val = sysconf(_SC_CLK_TCK);
      if (val == -1)
	return SysError(interp, argv[0], "-clk_tck");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'n' && !strncmp(argv[i], "-ng", 3)) {
      val = sysconf(_SC_NGROUPS_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-ngroups_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'o') {
      val = sysconf(_SC_OPEN_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-open_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'p' && !strncmp(argv[i], "-pas", 4)) {
      val = sysconf(_SC_PASS_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-pass_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'p' && !strncmp(argv[i], "-pag", 4)) {
      val = sysconf(_SC_PAGESIZE);
      if (val == -1)
	return SysError(interp, argv[0], "-pagesize");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'j') {
      val = sysconf(_SC_JOB_CONTROL);
      if (val == -1)
	return SysError(interp, argv[0], "-job_control");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 's' && !strncmp(argv[i], "-sa", 3)) {
      val = sysconf(_SC_SAVED_IDS);
      if (val == -1)
	return SysError(interp, argv[0], "-saved_ids");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'v') {
      val = sysconf(_SC_VERSION);
      if (val == -1)
	return SysError(interp, argv[0], "-version");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'x') {
      val = sysconf(_SC_XOPEN_VERSION);
      if (val == -1)
	return SysError(interp, argv[0], "-xopen_version");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'l') {
      val = sysconf(_SC_LOGNAME_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-logname_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'n' && !strncmp(argv[i], "-nprocessors_c", 14)) {
      val = sysconf(_SC_NPROCESSORS_CONF);
      if (val == -1)
	return SysError(interp, argv[0], "-nprocessors_conf");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'n' && !strncmp(argv[i], "-nprocessors_o", 14)) {
      val = sysconf(_SC_NPROCESSORS_ONLN);
      if (val == -1)
	return SysError(interp, argv[0], "-nprocessors_onln");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'p' && !strncmp(argv[i], "-ph", 3)) {
      val = sysconf(_SC_PHYS_PAGES);
      if (val == -1)
	return SysError(interp, argv[0], "-phys_pages");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'a' && !strncmp(argv[i], "-av", 3)) {
      val = sysconf(_SC_AVPHYS_PAGES);
      if (val == -1)
	return SysError(interp, argv[0], "-avphys_pages");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'a' && !strncmp(argv[i], "-aio_l", 6)) {
      val = sysconf(_SC_AIO_LISTIO_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-aio_listio_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'a' && !strncmp(argv[i], "-aio_m", 6)) {
      val = sysconf(_SC_AIO_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-aio_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'a' && !strncmp(argv[i], "-aio_p", 6)) {
      val = sysconf(_SC_AIO_PRIO_DELTA_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-aio_prio_delta_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'd') {
      val = sysconf(_SC_DELAYTIMER_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-delaytimer_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'm' && !strncmp(argv[i], "-mq_o", 5)) {
      val = sysconf(_SC_MQ_OPEN_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-mq_open_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'm' && !strncmp(argv[i], "-mq_p", 5)) {
      val = sysconf(_SC_MQ_PRIO_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-mq_prio_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'r' && !strncmp(argv[i], "-rt", 3)) {
      val = sysconf(_SC_RTSIG_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-rtsig_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 's' && !strncmp(argv[i], "-sem_n", 6)) {
      val = sysconf(_SC_SEM_NSEMS_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-sem_nsems_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 's' && !strncmp(argv[i], "-sem_v", 6)) {
      val = sysconf(_SC_SEM_VALUE_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-sem_value_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 's' && !strncmp(argv[i], "-si", 3)) {
      val = sysconf(_SC_SIGQUEUE_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-sigqueue_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 't' && !strncmp(argv[i], "-timer_", 7)) {
      val = sysconf(_SC_TIMER_MAX);
      if (val == -1)
	return SysError(interp, argv[0], "-timer_max");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'a' && !strncmp(argv[i], "-as", 3)) {
      val = sysconf(_SC_ASYNCHRONOUS_IO);
      if (val == -1)
	return SysError(interp, argv[0], "-asynchronous_io");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'f') {
      val = sysconf(_SC_FSYNC);
      if (val == -1)
	return SysError(interp, argv[0], "-fsync");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'm' && !strncmp(argv[i], "-ma", 3)) {
      val = sysconf(_SC_MAPPED_FILES);
      if (val == -1)
	return SysError(interp, argv[0], "-mapped_files");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'm' && !strcmp(argv[i], "-memlock")) {
      val = sysconf(_SC_MEMLOCK);
      if (val == -1)
	return SysError(interp, argv[0], "-memlock");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'm' && !strncmp(argv[i], "-memlock_", 9)) {
      val = sysconf(_SC_MEMLOCK_RANGE);
      if (val == -1)
	return SysError(interp, argv[0], "-memlock_range");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'm' && !strncmp(argv[i], "-memory_p", 9)) {
      val = sysconf(_SC_MEMORY_PROTECTION);
      if (val == -1)
	return SysError(interp, argv[0], "-memory_protection");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'm' && !strncmp(argv[i], "-mes", 4)) {
      val = sysconf(_SC_MESSAGE_PASSING);
      if (val == -1)
	return SysError(interp, argv[0], "-message_passing");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'p' && !strncmp(argv[i], "-prioriti", 9)) {
      val = sysconf(_SC_PRIORITIZED_IO);
      if (val == -1)
	return SysError(interp, argv[0], "-prioritized_io");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'p' && !strncmp(argv[i], "-priority", 9)) {
      val = sysconf(_SC_PRIORITY_SCHEDULING);
      if (val == -1)
	return SysError(interp, argv[0], "-priority_scheduling");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 'r' && !strncmp(argv[i], "-re", 3)) {
      val = sysconf(_SC_REALTIME_SIGNALS);
      if (val == -1)
	return SysError(interp, argv[0], "-realtime_signals");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 's' && !strncmp(argv[i], "-sema", 5)) {
      val = sysconf(_SC_SEMAPHORES);
      if (val == -1)
	return SysError(interp, argv[0], "-semaphores");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 's' && !strncmp(argv[i], "-sh", 3)) {
      val = sysconf(_SC_SHARED_MEMORY_OBJECTS);
      if (val == -1)
	return SysError(interp, argv[0], "-shared_memory_objects");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 's' && !strncmp(argv[i], "-sy", 3)) {
      val = sysconf(_SC_SYNCHRONIZED_IO);
      if (val == -1)
	return SysError(interp, argv[0], "-synchronized_io");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else if (c == 't' && !strcmp(argv[i], "-timers")) {
      val = sysconf(_SC_TIMERS);
      if (val == -1)
	return SysError(interp, argv[0], "-timers");
      sprintf(buf, "%d", val);
      Tcl_DStringAppendElement(&dstr, buf);
    }
    else {
      sprintf(interp->result,
	      "ambiguous or bad option \"%s\": must be one of "
	      "-arg_max, -child_max, -clk_tck, -ngroups_max, -open_max, "
	      "-pass_max, -pagesize, -job_control, -saved_ids, -version, "
	      "-xopen_version, -logname_max, "
	      "-nprocessors_conf, -nprocessors_onln, "
	      "-phys_pages, -avphys_pages, -aio_listio_max, -aio_max, "
	      "-aio_prio_delta_max, -delaytimer_max, "
	      "-mq_open_max, -mq_prio_max, -rtsig_max, "
	      "-sem_nsems_max, -sem_value_max, -sigqueue_max, "
	      "-timer_max, -asynchronous_io, -fsync, -mapped_files, "
	      "-memlock, -memlock_range, -memory_protection, "
	      "-message_passing, -prioritized_io, -priority_scheduling, "
	      "-realtime_signals, -semaphores, -shared_memory_objects, "
	      "-synchronized_io, or -timers",
	      argv[i]);
      Tcl_DStringFree(&dstr);
      return TCL_ERROR;
    }
  }

  Tcl_SetResult(interp, Tcl_DStringValue(&dstr), TCL_VOLATILE);
  Tcl_DStringFree(&dstr);

  return TCL_OK;
}
