示例#1
0
static void
sockaddr_set_data (struct sockaddr *sa, const ip_address *ip, int port)
{
  switch (ip->family)
    {
    case AF_INET:
      {
        struct sockaddr_in *sin = (struct sockaddr_in *)sa;
        xzero (*sin);
        sin->sin_family = AF_INET;
        sin->sin_port = htons (port);
        sin->sin_addr = ip->data.d4;
        break;
      }
#ifdef ENABLE_IPV6
    case AF_INET6:
      {
        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
        xzero (*sin6);
        sin6->sin6_family = AF_INET6;
        sin6->sin6_port = htons (port);
        sin6->sin6_addr = ip->data.d6;
#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
        sin6->sin6_scope_id = ip->ipv6_scope;
#endif
        break;
      }
#endif /* ENABLE_IPV6 */
    default:
      abort ();
    }
}
示例#2
0
int
CONCORD_BDB_get (DB* db, const char* key, DBT* valdatum)
{
  DBT keydatum;
  int status = 0;

  /* DB Version 2 requires DBT's to be zeroed before use. */
  xzero (keydatum);
  xzero (*valdatum);

  keydatum.data = (char*)key;
  keydatum.size = strlen (key);

  status = db->get (db, NULL, &keydatum, valdatum, 0);
  return status;
}
示例#3
0
static void
mswindows_init_device (struct device *d, Lisp_Object UNUSED (props))
{
  HDC hdc;
  WNDCLASSEXW wc;

  call0 (Qmake_device_early_mswindows_entry_point);

  DEVICE_CLASS (d) = Qcolor;
  DEVICE_INFD (d) = DEVICE_OUTFD (d) = -1;
  init_baud_rate (d);
  init_one_device (d);

#ifdef NEW_GC
  d->device_data = XMSWINDOWS_DEVICE (ALLOC_NORMAL_LISP_OBJECT (mswindows_device));
#else /* not NEW_GC */
  d->device_data = xnew_and_zero (struct mswindows_device);
#endif /* not NEW_GC */
  hdc = CreateCompatibleDC (NULL);
  assert (hdc != NULL);
  DEVICE_MSWINDOWS_HCDC (d) = hdc;
  DEVICE_MSWINDOWS_FONTLIST (d) = mswindows_enumerate_fonts (hdc);
  DEVICE_MSWINDOWS_UPDATE_TICK (d) = GetTickCount ();

  /* Register the main window class */
  wc.cbSize = sizeof (wc);
  wc.style = CS_OWNDC;	/* One DC per window */
  wc.lpfnWndProc = (WNDPROC) mswindows_wnd_proc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = MSWINDOWS_WINDOW_EXTRA_BYTES;
  /* This must match whatever is passed to CreateWIndowEx, NULL is ok
     for this. */
  wc.hInstance = NULL;	
  wc.hIcon = qxeLoadIcon (qxeGetModuleHandle (NULL), XETEXT (XEMACS_CLASS));
  wc.hCursor = qxeLoadCursor (NULL, IDC_ARROW);
  /* Background brush is only used during sizing, when XEmacs cannot
     take over */
  wc.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE + 1);
  wc.lpszMenuName = NULL;

  wc.lpszClassName = (XELPTSTR) XETEXT (XEMACS_CLASS);
  wc.hIconSm = (HICON) qxeLoadImage (qxeGetModuleHandle (NULL),
				     XETEXT (XEMACS_CLASS),
				     IMAGE_ICON, 16, 16, 0);
  qxeRegisterClassEx (&wc);

#ifdef HAVE_WIDGETS
  xzero (wc);
  /* Register the main window class */
  wc.cbSize = sizeof (wc);
  wc.lpfnWndProc = (WNDPROC) mswindows_control_wnd_proc;
  wc.lpszClassName = (XELPTSTR) XETEXT (XEMACS_CONTROL_CLASS);
  wc.hInstance = NULL;
  qxeRegisterClassEx (&wc);
#endif

#if defined (HAVE_TOOLBARS) || defined (HAVE_WIDGETS)
  InitCommonControls ();
#endif
}
示例#4
0
const char *
inet_ntop (int af, const void *src, char *dst, socklen_t cnt)
{
  /* struct sockaddr can't accomodate struct sockaddr_in6. */
  union {
    struct sockaddr_in6 sin6;
    struct sockaddr_in sin;
  } sa;
  DWORD dstlen = cnt;
  size_t srcsize;

  xzero (sa);
  switch (af)
    {
    case AF_INET:
      sa.sin.sin_family = AF_INET;
      sa.sin.sin_addr = *(struct in_addr *) src;
      srcsize = sizeof (sa.sin);
      break;
    case AF_INET6:
      sa.sin6.sin6_family = AF_INET6;
      sa.sin6.sin6_addr = *(struct in6_addr *) src;
      srcsize = sizeof (sa.sin6);
      break;
    default:
      abort ();
    }

  if (WSAAddressToString ((struct sockaddr *) &sa, srcsize, NULL, dst, &dstlen) != 0)
    {
      errno = WSAGetLastError();
      return NULL;
    }
  return (const char *) dst;
}
示例#5
0
/* Reset the variables to default values.  */
static void
defaults (void)
{
  char *tmp;

  /* Most of the default values are 0 (and 0.0, NULL, and false).
     Just reset everything, and fill in the non-zero values.  Note
     that initializing pointers to NULL this way is technically
     illegal, but porting Wget to a machine where NULL is not all-zero
     bit pattern will be the least of the implementors' worries.  */
  xzero (opt);

  opt.cookies = true;
  opt.verbose = -1;
  opt.ntry = 20;
  opt.reclevel = 5;
  opt.add_hostdir = true;
  opt.netrc = true;
  opt.ftp_glob = true;
  opt.htmlify = true;
  opt.http_keep_alive = true;
  opt.use_proxy = true;
  tmp = getenv ("no_proxy");
  if (tmp)
    opt.no_proxy = sepstring (tmp);
  opt.allow_cache = true;

  opt.read_timeout = 900;
  opt.use_robots = true;

  opt.remove_listing = true;

  opt.dot_bytes = 1024;
  opt.dot_spacing = 10;
  opt.dots_in_line = 50;

  opt.dns_cache = true;
  opt.ftp_pasv = true;

#ifdef HAVE_SSL
  opt.check_cert = true;
#endif

  /* The default for file name restriction defaults to the OS type. */
#if defined(WINDOWS) || defined(MSDOS) || defined(__CYGWIN__)
  opt.restrict_files_os = restrict_windows;
#else
  opt.restrict_files_os = restrict_unix;
#endif
  opt.restrict_files_ctrl = true;
  opt.restrict_files_case = restrict_no_case_restriction;

  opt.max_redirect = 20;

  opt.visualwget_listening_port = -1; // VisualWget
}
示例#6
0
int
CONCORD_BDB_put (DB* db, const char* key, unsigned char* value)
{
  DBT keydatum, valdatum;
  int status = 0;

  /* DB Version 2 requires DBT's to be zeroed before use. */
  xzero (keydatum);
  xzero (valdatum);

  keydatum.data = (char*)key;
  keydatum.size = strlen (key);

  valdatum.data = value;
  valdatum.size = strlen ((char*)value);

  status = db->put (db, NULL, &keydatum, &valdatum, 0);
  return status;
}
示例#7
0
/*
 * Compress executable and output it in relocatable object format.
 */
void
kgzcmp(struct kgz_hdr *kh, const char *f1, const char *f2)
{
    struct iodesc idi, ido;
    struct kgz_hdr khle;

    if ((idi.fd = open(idi.fname = f1, O_RDONLY)) == -1)
	err(1, "%s", idi.fname);
    if ((ido.fd = open(ido.fname = f2, O_CREAT | O_TRUNC | O_WRONLY,
		       0666)) == -1)
	err(1, "%s", ido.fname);
    kh->ident[0] = KGZ_ID0;
    kh->ident[1] = KGZ_ID1;
    kh->ident[2] = KGZ_ID2;
    kh->ident[3] = KGZ_ID3;
    mk_data(&idi, &ido, kh,
	    (format == F_AOUT ? sizeof(struct kgz_aouthdr0) :
				sizeof(struct kgz_elfhdr)) +
	     sizeof(struct kgz_hdr));
    kh->dload &= 0xffffff;
    kh->entry &= 0xffffff;
    if (format == F_AOUT) {
	struct kgz_aouthdr0 ahdr0 = aouthdr0;
	struct kgz_aouthdr1 ahdr1 = aouthdr1;
	unsigned x = (sizeof(struct kgz_hdr) + kh->nsize) & (16 - 1);
	if (x) {
	    x = 16 - x;
	    xzero(&ido, x);
	}
	xwrite(&ido, &ahdr1, sizeof(ahdr1));
	ahdr0.a.a_data += kh->nsize + x;
	xseek(&ido, 0);
	xwrite(&ido, &ahdr0, sizeof(ahdr0));
    } else {
	struct kgz_elfhdr ehdr = elfhdr;
	ehdr.st[KGZ_ST_KGZ_NDATA].st_size = htole32(kh->nsize);
	ehdr.sh[KGZ_SH_DATA].sh_size =
	    htole32(le32toh(ehdr.sh[KGZ_SH_DATA].sh_size) + kh->nsize);
	xseek(&ido, 0);
	xwrite(&ido, &ehdr, sizeof(ehdr));
    }
    khle = *kh;
    khle.dload = htole32(khle.dload);
    khle.dsize = htole32(khle.dsize);
    khle.isize = htole32(khle.isize);
    khle.entry = htole32(khle.entry);
    khle.nsize = htole32(khle.nsize);
    xwrite(&ido, &khle, sizeof(khle));
    xclose(&ido);
    xclose(&idi);
}
示例#8
0
/*
 * "Load" an ELF-format executable.
 */
static int
ld_elf(const struct iodesc * idi, const struct iodesc * ido,
       struct kgz_hdr * kh, const Elf32_Ehdr * e)
{
    Elf32_Phdr p;
    size_t load, addr, n;
    unsigned x, i;

    load = addr = n = 0;
    for (x = i = 0; i < e->e_phnum; i++) {
	if (xread(idi, &p, sizeof(p),
		  e->e_phoff + i * e->e_phentsize) != e->e_phentsize)
	    return -1;
	if (p.p_type != PT_LOAD)
	    continue;
	if (!x)
	    load = addr = p.p_vaddr;
	else {
	    if (p.p_vaddr < addr)
		return -1;
	    n = p.p_vaddr - addr;
	    if (n) {
		xzero(ido, n);
		addr += n;
	    }
	}
	if (p.p_memsz < p.p_filesz)
	    return -1;
	n = p.p_memsz - p.p_filesz;
	xcopy(idi, ido, p.p_filesz, p.p_offset);
	addr += p.p_filesz;
	x++;
    }
    if (!x)
	return -1;
    kh->dload = load;
    kh->dsize = addr - load;
    kh->isize = kh->dsize + n;
    kh->entry = e->e_entry;
    return 0;
}
示例#9
0
文件: log.c 项目: DonCN/haiku
void
logprintf (enum log_options o, const char *fmt, ...)
{
  va_list args;
  struct logvprintf_state lpstate;
  bool done;

  check_redirect_output ();
  if (inhibit_logging)
    return;
  CHECK_VERBOSE (o);

  xzero (lpstate);
  do
    {
      va_start (args, fmt);
      done = log_vprintf_internal (&lpstate, fmt, args);
      va_end (args);
    }
  while (!done);
}
示例#10
0
static printf_arg_dynarr *
get_doprnt_args (printf_spec_dynarr *specs, va_list vargs)
{
  printf_arg_dynarr *args = Dynarr_new (printf_arg);
  union printf_arg arg;
  REGISTER int i;
  int args_needed = get_args_needed (specs);

  xzero (arg);
  for (i = 1; i <= args_needed; i++)
    {
      int j;
      char ch;
      struct printf_spec *spec = 0;

      for (j = 0; j < Dynarr_length (specs); j++)
	{
	  spec = Dynarr_atp (specs, j);
	  if (spec->argnum == i)
	    break;
	}

      if (j == Dynarr_length (specs))
	error ("No conversion spec for argument %d", i);

      ch = spec->converter;

      if (strchr (int_converters, ch))
	{
	  if (spec->l_flag)
	    arg.l = va_arg (vargs, long);
	  else
	    /* int even if ch == 'c' or spec->h_flag:
	       "the type used in va_arg is supposed to match the
	       actual type **after default promotions**."
	       Hence we read an int, not a short, if spec->h_flag. */
	    arg.l = va_arg (vargs, int);
	}
      else if (strchr (unsigned_int_converters, ch))
示例#11
0
文件: log.c 项目: DonCN/haiku
/* The same as logprintf(), but does anything only if opt.debug is
   true.  */
void
debug_logprintf (const char *fmt, ...)
{
  if (opt.debug)
    {
      va_list args;
      struct logvprintf_state lpstate;
      bool done;

      check_redirect_output ();
      if (inhibit_logging)
        return;

      xzero (lpstate);
      do
        {
          va_start (args, fmt);
          done = log_vprintf_internal (&lpstate, fmt, args);
          va_end (args);
        }
      while (!done);
    }
}
示例#12
0
int
gtk_parse_nearest_color (struct device *d, GdkColor *color, Bufbyte *name,
			 Bytecount len, Error_behavior errb)
{
  GdkColormap *cmap;
  GdkVisual *visual;
  int result;

  cmap = DEVICE_GTK_COLORMAP(d);
  visual = DEVICE_GTK_VISUAL (d);

  xzero (*color);
  {
    const Extbyte *extname;
    Extcount extnamelen;

    TO_EXTERNAL_FORMAT (DATA, (name, len), ALLOCA, (extname, extnamelen), Qbinary);

    result = gdk_color_parse (extname, color);
  }
  
  if (result == FALSE)
    {
      maybe_signal_simple_error ("unrecognized color", make_string (name, len),
				 Qcolor, errb);
      return 0;
    }
  result = allocate_nearest_color (cmap, visual, color);
  if (!result)
    {
      maybe_signal_simple_error ("couldn't allocate color",
				 make_string (name, len), Qcolor, errb);
      return 0;
    }

  return result;
}
示例#13
0
文件: host.c 项目: Red54/axtu
struct address_list *
lookup_host (const char *host, int flags)
{
  struct address_list *al;
  int silent = flags & LH_SILENT;
  int use_cache;
  int numeric_address = 0;
  double timeout = opt.dns_timeout;

#ifndef ENABLE_IPV6
  /* If we're not using getaddrinfo, first check if HOST specifies a
     numeric IPv4 address.  Some implementations of gethostbyname
     (e.g. the Ultrix one and possibly Winsock) don't accept
     dotted-decimal IPv4 addresses.  */
  {
    uint32_t addr_ipv4 = (uint32_t)inet_addr (host);
    if (addr_ipv4 != (uint32_t) -1)
      {
	/* No need to cache host->addr relation, just return the
	   address.  */
	char *vec[2];
	vec[0] = (char *)&addr_ipv4;
	vec[1] = NULL;
	return address_list_from_ipv4_addresses (vec);
      }
  }
#else  /* ENABLE_IPV6 */
  /* If we're using getaddrinfo, at least check whether the address is
     already numeric, in which case there is no need to print the
     "Resolving..." output.  (This comes at no additional cost since
     the is_valid_ipv*_address are already required for
     url_parse.)  */
  {
    const char *end = host + strlen (host);
    if (is_valid_ipv4_address (host, end) || is_valid_ipv6_address (host, end))
      numeric_address = 1;
  }
#endif

  /* Cache is normally on, but can be turned off with --no-dns-cache.
     Don't cache passive lookups under IPv6.  */
  use_cache = opt.dns_cache;
#ifdef ENABLE_IPV6
  if ((flags & LH_BIND) || numeric_address)
    use_cache = 0;
#endif

  /* Try to find the host in the cache so we don't need to talk to the
     resolver.  If LH_REFRESH is requested, remove HOST from the cache
     instead.  */
  if (use_cache)
    {
      if (!(flags & LH_REFRESH))
	{
	  al = cache_query (host);
	  if (al)
	    return al;
	}
      else
	cache_remove (host);
    }

  /* No luck with the cache; resolve HOST. */

  if (!silent && !numeric_address)
    logprintf (LOG_VERBOSE, _("Resolving %s... "), escnonprint (host));

#ifdef ENABLE_IPV6
  {
    int err;
    struct addrinfo hints, *res;

    xzero (hints);
    hints.ai_socktype = SOCK_STREAM;
    if (opt.ipv4_only)
      hints.ai_family = AF_INET;
    else if (opt.ipv6_only)
      hints.ai_family = AF_INET6;
    else
      /* We tried using AI_ADDRCONFIG, but removed it because: it
	 misinterprets IPv6 loopbacks, it is broken on AIX 5.1, and
	 it's unneeded since we sort the addresses anyway.  */
	hints.ai_family = AF_UNSPEC;

    if (flags & LH_BIND)
      hints.ai_flags |= AI_PASSIVE;

#ifdef AI_NUMERICHOST
    if (numeric_address)
      {
	/* Where available, the AI_NUMERICHOST hint can prevent costly
	   access to DNS servers.  */
	hints.ai_flags |= AI_NUMERICHOST;
	timeout = 0;		/* no timeout needed when "resolving"
				   numeric hosts -- avoid setting up
				   signal handlers and such. */
      }
#endif

    err = getaddrinfo_with_timeout (host, NULL, &hints, &res, timeout);
    if (err != 0 || res == NULL)
      {
	if (!silent)
	  logprintf (LOG_VERBOSE, _("failed: %s.\n"),
		     err != EAI_SYSTEM ? gai_strerror (err) : strerror (errno));
	return NULL;
      }
    al = address_list_from_addrinfo (res);
    freeaddrinfo (res);
    if (!al)
      {
	logprintf (LOG_VERBOSE,
		   _("failed: No IPv4/IPv6 addresses for host.\n"));
	return NULL;
      }

    /* Reorder addresses so that IPv4 ones (or IPv6 ones, as per
       --prefer-family) come first.  Sorting is stable so the order of
       the addresses with the same family is undisturbed.  */
    if (al->count > 1 && opt.prefer_family != prefer_none)
      stable_sort (al->addresses, al->count, sizeof (ip_address),
		   opt.prefer_family == prefer_ipv4
		   ? cmp_prefer_ipv4 : cmp_prefer_ipv6);
  }
#else  /* not ENABLE_IPV6 */
  {
    struct hostent *hptr = gethostbyname_with_timeout (host, timeout);
    if (!hptr)
      {
	if (!silent)
	  {
	    if (errno != ETIMEDOUT)
	      logprintf (LOG_VERBOSE, _("failed: %s.\n"),
			 host_errstr (h_errno));
	    else
	      logputs (LOG_VERBOSE, _("failed: timed out.\n"));
	  }
	return NULL;
      }
    /* Do older systems have h_addr_list?  */
    al = address_list_from_ipv4_addresses (hptr->h_addr_list);
  }
#endif /* not ENABLE_IPV6 */

  /* Print the addresses determined by DNS lookup, but no more than
     three.  */
  if (!silent && !numeric_address)
    {
      int i;
      int printmax = al->count <= 3 ? al->count : 3;
      for (i = 0; i < printmax; i++)
	{
	  logprintf (LOG_VERBOSE, "%s",
		     pretty_print_address (al->addresses + i));
	  if (i < printmax - 1)
	    logputs (LOG_VERBOSE, ", ");
	}
      if (printmax != al->count)
	logputs (LOG_VERBOSE, ", ...");
      logputs (LOG_VERBOSE, "\n");
    }

  /* Cache the lookup information. */
  if (use_cache)
    cache_store (host, al);

  return al;
}
示例#14
0
文件: retr.c 项目: catufunwa/wget
static void
limit_bandwidth_reset (void)
{
  xzero (limit_data);
}
示例#15
0
/* Windows doesn't support the fork() call; so we fake it by invoking
   another copy of Wget with the same arguments with which we were
   invoked.  The child copy of Wget should perform the same initialization
   sequence as the parent; so we should have two processes that are
   essentially identical.  We create a specially named section object that
   allows the child to distinguish itself from the parent and is used to
   exchange information between the two processes.  We use an event object
   for synchronization.  */
static void
fake_fork (void)
{
  char exe[MAX_PATH + 1];
  DWORD exe_len, le;
  SECURITY_ATTRIBUTES sa;
  HANDLE section, event, h[2];
  STARTUPINFO si;
  PROCESS_INFORMATION pi;
  struct fake_fork_info *info;
  char *name;
  BOOL rv;

  section = pi.hProcess = pi.hThread = NULL;

  /* Get the fully qualified name of our executable.  This is more reliable
     than using argv[0].  */
  exe_len = GetModuleFileName (GetModuleHandle (NULL), exe, sizeof (exe));
  if (!exe_len || (exe_len >= sizeof (exe)))
    return;

  sa.nLength = sizeof (sa);
  sa.lpSecurityDescriptor = NULL;
  sa.bInheritHandle = TRUE;

  /* Create an anonymous inheritable event object that starts out
     non-signaled.  */
  event = CreateEvent (&sa, FALSE, FALSE, NULL);
  if (!event)
    return;

  /* Create the child process detached form the current console and in a
     suspended state.  */
  xzero (si);
  si.cb = sizeof (si);
  rv = CreateProcess (exe, GetCommandLine (), NULL, NULL, TRUE,
                      CREATE_SUSPENDED | DETACHED_PROCESS,
                      NULL, NULL, &si, &pi);
  if (!rv)
    goto cleanup;

  /* Create a named section object with a name based on the process id of
     the child.  */
  name = make_section_name (pi.dwProcessId);
  section =
      CreateFileMapping (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
                         sizeof (struct fake_fork_info), name);
  le = GetLastError();
  xfree (name);
  /* Fail if the section object already exists (should not happen).  */
  if (!section || (le == ERROR_ALREADY_EXISTS))
    {
      rv = FALSE;
      goto cleanup;
    }

  /* Copy the event handle into the section object.  */
  info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
  if (!info)
    {
      rv = FALSE;
      goto cleanup;
    }

  info->event = event;

  UnmapViewOfFile (info);

  /* Start the child process.  */
  rv = ResumeThread (pi.hThread);
  if (!rv)
    {
      TerminateProcess (pi.hProcess, (DWORD) -1);
      goto cleanup;
    }

  /* Wait for the child to signal to us that it has done its part.  If it
     terminates before signaling us it's an error.  */

  h[0] = event;
  h[1] = pi.hProcess;
  rv = WAIT_OBJECT_0 == WaitForMultipleObjects (2, h, FALSE, 5 * 60 * 1000);
  if (!rv)
    goto cleanup;

  info = MapViewOfFile (section, FILE_MAP_READ, 0, 0, 0);
  if (!info)
    {
      rv = FALSE;
      goto cleanup;
    }

  /* Ensure string is properly terminated.  */
  if (info->logfile_changed &&
      !memchr (info->lfilename, '\0', sizeof (info->lfilename)))
    {
      rv = FALSE;
      goto cleanup;
    }

  printf (_("Continuing in background, pid %lu.\n"), pi.dwProcessId);
  if (info->logfile_changed)
    printf (_("Output will be written to `%s'.\n"), info->lfilename);

  UnmapViewOfFile (info);

cleanup:

  if (event)
    CloseHandle (event);
  if (section)
    CloseHandle (section);
  if (pi.hThread)
    CloseHandle (pi.hThread);
  if (pi.hProcess)
    CloseHandle (pi.hProcess);

  /* We're the parent.  If all is well, terminate.  */
  if (rv)
    exit (0);

  /* We failed, return.  */
}
示例#16
0
static printf_spec_dynarr *
parse_doprnt_spec (const Bufbyte *format, Bytecount format_length)
{
  const Bufbyte *fmt = format;
  const Bufbyte *fmt_end = format + format_length;
  printf_spec_dynarr *specs = Dynarr_new (printf_spec);
  int prev_argnum = 0;

  while (1)
    {
      struct printf_spec spec;
      const Bufbyte *text_end;
      Bufbyte ch;

      xzero (spec);
      if (fmt == fmt_end)
	return specs;
      text_end = (Bufbyte *) memchr (fmt, '%', fmt_end - fmt);
      if (!text_end)
	text_end = fmt_end;
      spec.text_before = fmt - format;
      spec.text_before_len = text_end - fmt;
      fmt = text_end;
      if (fmt != fmt_end)
	{
	  fmt++; /* skip over % */

	  /* A % is special -- no arg number.  According to ANSI specs,
	     field width does not apply to %% conversion. */
	  if (fmt != fmt_end && *fmt == '%')
	    {
	      spec.converter = '%';
	      Dynarr_add (specs, spec);
	      fmt++;
	      continue;
	    }

	  /* Is there a field number specifier? */
	  {
	    const Bufbyte *ptr;
	    int fieldspec;

	    ptr = parse_off_posnum (fmt, fmt_end, &fieldspec);
	    if (fieldspec > 0 && ptr != fmt_end && *ptr == '$')
	      {
		/* There is a format specifier */
		prev_argnum = fieldspec;
		fmt = ptr + 1;
	      }
	    else
	      prev_argnum++;
	    spec.argnum = prev_argnum;
	  }

	  /* Parse off any flags */
	  NEXT_ASCII_BYTE (ch);
	  while (strchr (valid_flags, ch))
	    {
	      switch (ch)
		{
		case '-': spec.minus_flag  = 1; break;
		case '+': spec.plus_flag   = 1; break;
		case ' ': spec.space_flag  = 1; break;
		case '#': spec.number_flag = 1; break;
		case '0': spec.zero_flag   = 1; break;
		default: ABORT ();
		}
	      NEXT_ASCII_BYTE (ch);
	    }

	  /* Parse off the minimum field width */
	  fmt--; /* back up */

	  /*
	   * * means the field width was passed as an argument.
	   * Mark the current spec as one that forwards its
	   * field width and flags to the next spec in the array.
	   * Then create a new spec and continue with the parsing.
	   */
	  if (fmt != fmt_end && *fmt == '*')
	    {
	      spec.converter = '*';
	      RESOLVE_FLAG_CONFLICTS(spec);
	      Dynarr_add (specs, spec);
	      xzero (spec);
	      spec.argnum = ++prev_argnum;
	      fmt++;
	    }
	  else
	    {
	      fmt = parse_off_posnum (fmt, fmt_end, &spec.minwidth);
	      if (spec.minwidth == -1)
		spec.minwidth = 0;
	    }

	  /* Parse off any precision specified */
	  NEXT_ASCII_BYTE (ch);
	  if (ch == '.')
	    {
	      /*
	       * * means the precision was passed as an argument.
	       * Mark the current spec as one that forwards its
	       * fieldwidth, flags and precision to the next spec in
	       * the array.  Then create a new spec and continue
	       * with the parse.
	       */
	      if (fmt != fmt_end && *fmt == '*')
		{
		  spec.converter = '*';
		  spec.forwarding_precision = 1;
		  RESOLVE_FLAG_CONFLICTS(spec);
		  Dynarr_add (specs, spec);
		  xzero (spec);
		  spec.argnum = ++prev_argnum;
		  fmt++;
		}
	      else
		{
		  fmt = parse_off_posnum (fmt, fmt_end, &spec.precision);
		  if (spec.precision == -1)
		    spec.precision = 0;
		}
	      NEXT_ASCII_BYTE (ch);
	    }
	  else
	    /* No precision specified */
	    spec.precision = -1;

	  /* Parse off h or l flag */
	  if (ch == 'h' || ch == 'l')
	    {
	      if (ch == 'h')
		spec.h_flag = 1;
	      else
		spec.l_flag = 1;
	      NEXT_ASCII_BYTE (ch);
	    }

	  if (!strchr (valid_converters, ch))
	    error ("Invalid converter character %c", ch);
	  spec.converter = ch;
	}

      RESOLVE_FLAG_CONFLICTS(spec);
      Dynarr_add (specs, spec);
    }

  RETURN_NOT_REACHED(specs) /* suppress compiler warning */
}