Example #1
0
/**
 * trace_seq_printf - sequence printing of trace information
 * @s: trace sequence descriptor
 * @fmt: printf format string
 *
 * It returns 0 if the trace oversizes the buffer's free
 * space, 1 otherwise.
 *
 * The tracer may use either sequence operations or its own
 * copy to user routines. To simplify formating of a trace
 * trace_seq_printf is used to store strings into a special
 * buffer (@s). Then the output may be either used by
 * the sequencer or pulled into another buffer.
 */
int
trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
{
	va_list ap;
	int len;
	int ret;

	TRACE_SEQ_CHECK(s);

 try_again:
	len = (s->buffer_size - 1) - s->len;

	va_start(ap, fmt);
	ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
	va_end(ap);

	if (ret >= len) {
		expand_buffer(s);
		goto try_again;
	}

	s->len += ret;

	return 1;
}
Example #2
0
static bool_t burp_putbytes(XDR* xdrs, const SCHAR* buff, u_int bytecount)
{
/**************************************
 *
 *	b u r p _ p u t b y t e s
 *
 **************************************
 *
 * Functional description
 *	Fetch a bunch of bytes into a memory stream if it fits.
 *
 **************************************/

	if (bytecount && xdrs->x_handy >= (int) bytecount)
	{
		xdrs->x_handy -= bytecount;
		do {
			*xdrs->x_private++ = *buff++;
		} while (--bytecount);
		return TRUE;
	}

	while (bytecount)
	{
		if (xdrs->x_handy <= 0 && !expand_buffer(xdrs))
		{
			return FALSE;
		}
		--xdrs->x_handy;
		*xdrs->x_private++ = *buff++;
		--bytecount;
	}

	return TRUE;
}
Example #3
0
	void write(const char* buf, unsigned int len)
	{
		if(base::alloc - base::size < len) {
			expand_buffer(len);
		}
		memcpy(base::data + base::size, buf, len);
		base::size += len;
	}
Example #4
0
	inline void write(const char* buf, size_t len)
	{
		if(m_allocated - m_used < len) {
			expand_buffer(len);
		}
		memcpy(m_storage + m_used, buf, len);
		m_used += len;
	}
Example #5
0
 void append(const char* buf, std::size_t len)
 {
     if(_alloc - _size < len)
     {
         expand_buffer(len);
     }
     memcpy(_data + _size, buf, len);
     _size += len;
 }
Example #6
0
int trace_seq_putc(struct trace_seq *s, unsigned char c)
{
	TRACE_SEQ_CHECK(s);

	while (s->len >= (s->buffer_size - 1))
		expand_buffer(s);

	s->buffer[s->len++] = c;

	return 1;
}
Example #7
0
/*
 * Function used by YAP to write a char to a string
 */
static void
p2c_putc(const int c) {
  //  if( buffer.size==buffer.len+1 ) 
  if( BUFFER_SIZE==BUFFER_LEN ) { 
#ifdef DEBUG
  write_msg(__FUNCTION__,__FILE__,__LINE__,"p2c_putc:buffer expanded: size=%u pos=%u len=%u\n",BUFFER_SIZE,BUFFER_POS,BUFFER_LEN);  
#endif
     expand_buffer( BLOCK_SIZE );    
  }
  BUFFER_PTR[BUFFER_LEN++] = c;   
}
Example #8
0
/*
 * Function used by YAP to write a char to a string
 */
static void
p2c_putt(const YAP_Term t) {
  //  if( buffer.size==buffer.len+1 ) 

  while ((BUFFER_LEN=YAP_ExportTerm(t, BUFFER_PTR, BUFFER_SIZE)) <= 0) {
#ifdef DEBUG
     write_msg(__FUNCTION__,__FILE__,__LINE__,"p2c_putc:buffer expanded: size=%u pos=%u len=%u\n",BUFFER_SIZE,BUFFER_POS,BUFFER_LEN);  
#endif
     expand_buffer( BLOCK_SIZE );    
  }
}
Example #9
0
    void append(const char c, std::size_t count)
    {
        if(count)
        {
            if(_alloc - _size < count)
            {
                expand_buffer(count);
            }

            std::memset(_data + _size, c, count);
            _size += count;
        }
    }
Example #10
0
/*
 * Read a prolog term from a stream
 * (the prolog term must have been writen by the write_term_to_stream)
 */
YAP_Term 
read_term_from_stream(const int fd) {
  size_t size; 

  RESET_BUFFER;    
  read(fd,(void*)&size,sizeof(size_t)); // read the size of the term
#ifdef DEBUG
  write_msg(__FUNCTION__,__FILE__,__LINE__,"read_term_from_stream>>>>size:%d\n",size);  
#endif
  if ( size> BUFFER_SIZE)
    expand_buffer(size-BUFFER_SIZE);
  read(fd,BUFFER_PTR,size);            // read term from stream
  return YAP_Read( p2c_getc );
}
Example #11
0
/* helper for print_special_fp(): appends string, pads with space if needed */
static void append_padded(struct string_builder *bld, const char *str, int width)
{
	size_t len = strlen(str);
	size_t tmpwidth = width < 0 ? 0 : width;

	if (tmpwidth > len) {
		size_t pad = tmpwidth - len;
		expand_buffer(bld, pad);
		while (pad--) {
			bld->buf[bld->len++] = ' ';
		}
	}

	append_string(bld, str, len);
}
Example #12
0
/**
 * trace_seq_puts - trace sequence printing of simple string
 * @s: trace sequence descriptor
 * @str: simple string to record
 *
 * The tracer may use either the sequence operations or its own
 * copy to user routines. This function records a simple string
 * into a special buffer (@s) for later retrieval by a sequencer
 * or other mechanism.
 */
int trace_seq_puts(struct trace_seq *s, const char *str)
{
	int len;

	TRACE_SEQ_CHECK(s);

	len = strlen(str);

	while (len > ((s->buffer_size - 1) - s->len))
		expand_buffer(s);

	memcpy(s->buffer + s->len, str, len);
	s->len += len;

	return len;
}
Example #13
0
/*
 * Read a prolog term from a stream
 * (the prolog term must have been writen by the write_term_to_stream)
 */
YAP_Term 
read_term_from_stream(const int fd) {
  size_t size; 

  RESET_BUFFER();    
  if (!read(fd,(void*)&size,sizeof(size_t))) { // read the size of the term
    YAP_Error(0,0,"Prolog2Term: IO error in read.\n");
  }
#ifdef DEBUG
  write_msg(__FUNCTION__,__FILE__,__LINE__,"read_term_from_stream>>>>size:%d\n",size);  
#endif
  if ( size> BUFFER_SIZE)
    expand_buffer(size-BUFFER_SIZE);
  if (!read(fd,BUFFER_PTR,size)) {
    YAP_Error(0,0,"Prolog2Term: IO error in read.\n");
  };            // read term from stream
  return YAP_ImportTerm( BUFFER_PTR);
}
Example #14
0
/*
 * Converts a term t into a string.  
 * The ascii representation of t is
 * copied to ptr if it occupies less than size. 
 */
char* 
term2string(char *const ptr, size_t *size, const YAP_Term t) {
  char *ret;
  RESET_BUFFER();

  do {
    if (*size == 0) {
      *size = BUFFER_LEN = YAP_ExportTerm( t, BUFFER_PTR, BUFFER_SIZE );// canonical
      ret=BUFFER_PTR;
      if (BUFFER_LEN == 0) {
	expand_buffer(BLOCK_SIZE);
      }
    } else {
      *size = YAP_ExportTerm( t, ptr, BUFFER_SIZE );// canonical
      ret=ptr;
    }
  } while (*size <= 0);
  return ret;
}
Example #15
0
/**
 * trace_seq_vprintf - sequence printing of trace information
 * @s: trace sequence descriptor
 * @fmt: printf format string
 *
 * The tracer may use either sequence operations or its own
 * copy to user routines. To simplify formating of a trace
 * trace_seq_printf is used to store strings into a special
 * buffer (@s). Then the output may be either used by
 * the sequencer or pulled into another buffer.
 */
int
trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
{
	int len;
	int ret;

	TRACE_SEQ_CHECK(s);

 try_again:
	len = (s->buffer_size - 1) - s->len;

	ret = vsnprintf(s->buffer + s->len, len, fmt, args);

	if (ret >= len) {
		expand_buffer(s);
		goto try_again;
	}

	s->len += ret;

	return len;
}
Example #16
0
static void __attribute__ ((optimize("-O3"))) refresh_screen (SDL_Surface *scr)
// uses global line + vga_odd, scale two times
{

    uint32_t * restrict dst = (uint32_t*)scr->pixels; // will render 2 pixels at a time horizontally

    draw_buffer = mybuffer1; // currently 16bit data

    for (vga_line=0;vga_line<screen_height;vga_line++) {
        #ifdef VGA_SKIPLINE
            vga_odd=0;
            graph_line(); // using line, updating draw_buffer ...
            #if VGA_BPP==8
            expand_buffer();
            #endif 
            vga_odd=1; 
            graph_line(); //  a second time for SKIPLINE modes
            #if VGA_BPP==8
            expand_buffer();
            #endif 
        #else 
            graph_line(); 
            #if VGA_BPP==8
            expand_buffer();
            #endif 
        #endif

        // copy to screen at this position
        uint16_t *restrict src = (uint16_t*) draw_buffer;
        switch (scale) {
            case 1 : 
                // copy to screen at this position (cheating)
                for (int i=0;i<screen_width;i++)
                    *dst++= pixelconv32(*src++);
                break;

            case 2 : 
                for (int i=0;i<screen_width;i++, dst+=2) {
                    uint32_t pix = pixelconv32(*src++);
                    *dst = pix; // blit line
                    *(dst+1) = pix; // blit line
                    
                    *(dst+scr->pitch/sizeof(uint32_t))=pix; // also next line
                    *(dst+scr->pitch/sizeof(uint32_t)+1)=pix; // also next line
                    
                }
                dst += scr->pitch/sizeof(uint32_t); // we already drew the line after, skip it
                break;
        } 


        // swap lines buffers to simulate double line buffering
        draw_buffer = ( draw_buffer == &mybuffer1[0] ) ? &mybuffer2[0] : &mybuffer1[0];

    }
    for (;vga_line<screen_height+VSYNC_LINES;vga_line++) {
        #ifdef VGA_SKIPLINE
            vga_odd=0;
            graph_vsync(); // using line, updating draw_buffer ...
            vga_odd=1; 
            graph_vsync(); //  a second time for SKIPLINE modes
        #else 
            graph_vsync(); // once
        #endif
    }
}
Example #17
0
File: thread.c Project: zhyg/mapred
void event_handler(int fd, short e, void* args)
{
    char*   line =  NULL;
    int     len  =  0; 
    int     size =  0;

    int st              = wet->st;
    IOstream* stream    = &(wet->stream);
    WEVENT_T* ev        = (WEVENT_T*)args;

    if (!isempty_buffer(&ev->buffer)) {
        size = cwrite(fd, ev->buffer.cur, ev->buffer.size);
        if (size < 0) {
            log("write error, %s\n", strerror(errno));
            update_stat(STAT_CLOSE_PIPE);
        } else {
            seek_buffer(&ev->buffer, size);
            return;
        }
    }

    switch (st) {
    case STAT_READ_MORE:
        if (try_read_more(stream) == E_ERROR) {
            update_stat(STAT_LAST_BUF);
        } else {
            update_stat(STAT_WRITE_LINE);
        }
        break;

    case STAT_WRITE_LINE:
        if (get_line(stream, &line, &len) == E_NEED_MORE || len == 0) {
            update_stat(STAT_READ_MORE);
            break;
        }
    
        size = cwrite(fd, line, len);
        if (size < 0) {
            log("write error, %s\n", strerror(errno));
            update_stat(STAT_CLOSE_PIPE);
            break;
        }

        if (size < len) {
            expand_buffer(&ev->buffer, line + size, len - size);
            break;
        }
        break;

    case STAT_LAST_BUF:
        if (stream->bytes <= 0) {
            update_stat(STAT_CLOSE_PIPE);
            break;
        }
        size = cwrite(fd, stream->cur, stream->bytes);
        if (size < 0) {
            log("write error, %s\n", strerror(errno));
            update_stat(STAT_CLOSE_PIPE);
            break;
        }

        if (size == stream->bytes) {
            stream->bytes = 0;
            update_stat(STAT_CLOSE_PIPE);
            break;
        }

        if (size < stream->bytes) {
            expand_buffer(&ev->buffer, stream->cur+size, stream->bytes-size);
            stream->bytes = 0;
            break;
        }
        break;

    case STAT_CLOSE_PIPE:
        close(fd);
        event_del(&ev->e);
        break;
    } 
}
Example #18
0
int paralign_score(buffer_t* buffer,
                   const submat_t<score_t> submat,
                   const score_t gap_open,
                   const score_t gap_extend,
                   const seq_t seq,
                   const seq_t* refs,
                   const int n_refs,
                   alignment_t** alignments)
{
    if (n_refs == 0)
        return 0;
    else if (n_refs < 0)
        return 1;

    // allocate working space
    if (expand_buffer(buffer, sizeof(vec_t) * (seq.len + 1) * 2 +
                              sizeof(vec_t) * submat.size +
                              sizeof(uint8_t) * seq.len)) {
        return 1;
    }
    // NOTE: colE[0] is not used
    vec_t* colE = (vec_t*)buffer->data;
    vec_t* colH = colE + seq.len + 1;
    vec_t* prof = colH + seq.len + 1;
    uint8_t* useq = reinterpret_cast<uint8_t*>(prof + submat.size);

    // unpack sequence
    for (size_t i = 0; i < seq.len; i++)
        useq[i] = seq[i];

    // initialize slots which hold the reference sequences
    const int n_max_par = sizeof(vec_t) / sizeof(score_t);
    std::array<slot_t,n_max_par> slots;
    slots.fill(empty_slot);
    int next_ref = 0;

    // outer loop along refs
    while (true) {
        // initialize the slots and the column vectors
        for (int k = 0; k < n_max_par; k++) {
            slot_t &slot = slots[k];

            if (slot != empty_slot) {
                slot.pos++;
                if (slot.pos < refs[slot.id].len)
                    continue;
                else
                    (*alignments[slot.id]).score = simd_extract<score_t>(colH[seq.len], k);
            }

            // find the next non-empty sequences if any
            bool found = false;
            while (next_ref < n_refs && !found) {
                // reset E and H
                colH[0] = simd_insert<score_t>(colH[0], 0, k);
                for (int i = 1; i <= seq.len; i++) {
                    score_t h = affine_gap_score(i, gap_open, gap_extend);
                    colH[i] = simd_insert(colH[i], h, k);
                    colE[i] = simd_insert(colE[i], static_cast<score_t>(h - (gap_open + gap_extend)), k);
                }
                seq_t ref = refs[next_ref];
                if (ref.len == 0) {
                    (*alignments[next_ref++]).score = simd_extract<score_t>(colH[seq.len], k);
                }
                else {
                    slot.id = next_ref++;
                    slot.pos = 0;
                    found = true;
                }
            }

            if (!found)
                slots[k] = empty_slot;
        }

        // check if there are remaining slots
        if (is_vacant(slots))
            break;

        // fill the temporary profile
        fill_profile(refs, slots, submat, prof);

        // inner loop along seq
        // TODO: detect saturation
        loop(useq, seq.len, prof, slots, gap_open, gap_extend, colE, colH);
    }

    return 0;
}
Example #19
0
/* appends a literal string */
static void append_string(struct string_builder *bld, const char *str, size_t len)
{
	expand_buffer(bld, len);
	memcpy(bld->buf + bld->len, str, len);
	bld->len += len;
}
Example #20
0
/* the actual string format parser
 * Although it's not in the documentation of `printf()`, but in addition to the
 * `%d` conversion specifier, this supports `%i`, which takes an `int` argument
 * instead of a `long`. It is used only for formatting error messages (since
 * Sparkling integers are all `long`s), but feel free to use it yourself.
 *
 * if `errmsg' is not a NULL pointer, and an error occurred while creating the
 * format string, then on return, `*errmsg' will point to a string containing
 * a message that describes the error.
 */
static char *make_format_string(
	const char *fmt,
	size_t *len,
	int argc,
	void *argv,
	int isval,
	char **errmsg
)
{
	struct string_builder bld;
	int argidx = 0;
	const char *s = fmt;
	const char *p = s;	/* points to the beginning of the next
				 * non-format part of the format string
				 */

	init_builder(&bld);

	while (*s) {
		if (*s == '%') {
			struct format_args args;
			init_format_args(&args);

			/* append preceding non-format string chunk */
			if (s > p) {
				append_string(&bld, p, s - p);
			}

			s++;

			/* Actually parse the format string.
			 * '#' flag: prepend base prefix (0b, 0, 0x)
			 */
			if (*s == '#') {
				args.flags |= FLAG_BASEPREFIX;
				s++;
			}

			/* ' ' (space) flag: prepend space if non-negative
			 * '+' flag: always prepend explicit + or - sign
			 */
			if (*s == ' ') {
				args.flags |= FLAG_PADSIGN;
				s++;
			} else if (*s == '+') {
				args.flags |= FLAG_EXPLICITSIGN;
				s++;
			}

			/* leading 0 flag: pad field with zeroes */
			if (*s == '0') {
				args.flags |= FLAG_ZEROPAD;
				s++;
			}

			/* field width specifier */
			if (isdigit(*s)) {
				args.width = 0;
				while (isdigit(*s)) {
					args.width *= 10;
					args.width += *s++ - '0';
				}
			} else if (*s == '*') {
				s++;
				if (isval) {
					SpnValue *widthptr;

					/* check argc if the caller wants us to do so */
					if (argc >= 0 && argidx >= argc) {
						format_errmsg(errmsg, OUT_OF_ARGUMENTS, argidx);
						free(bld.buf);
						return NULL;
					}

					/* width specifier must be an integer */
					widthptr = getarg_val(argv, &argidx);
					if (!isnum(widthptr)) {
					 	format_errmsg(
					 		errmsg,
					 		TYPE_MISMATCH,
					 		argidx,
					 		SPN_TTAG_NUMBER,
					 		widthptr->type
					 	);
						free(bld.buf);
						return NULL;
					}

					if (isfloat(widthptr)) {
				 		format_errmsg(
				 			errmsg,
				 			EXPECT_INTEGER,
				 			argidx
				 		);
						free(bld.buf);
						return NULL;
					}

					args.width = intvalue(widthptr);
				} else {
					const int *widthptr = getarg_raw(argv, &argidx);
					args.width = *widthptr;
				}
			}

			/* precision/maximal length specifier */
			if (*s == '.') {
				s++;

				if (*s == '+') {
					args.flags |= FLAG_EXPONENTSIGN;
					s++;
				}

				args.precision = 0;
				if (isdigit(*s)) {
					while (isdigit(*s)) {
						args.precision *= 10;
						args.precision += *s++ - '0';
					}
				} else if (*s == '*') {
					s++;
					if (isval) {
						SpnValue *precptr;

						/* check argc if the caller wants us to do so */
						if (argc >= 0 && argidx >= argc) {
							format_errmsg(errmsg, OUT_OF_ARGUMENTS, argidx);
							free(bld.buf);
							return NULL;
						}

						/* precision must be an integer too */
						precptr = getarg_val(argv, &argidx);

						if (!isnum(precptr)) {
							format_errmsg(
								errmsg,
								TYPE_MISMATCH,
								argidx,
								SPN_TTAG_NUMBER,
								precptr->type
							);
							free(bld.buf);
							return NULL;
						}

						if (isfloat(precptr)) {
					 		format_errmsg(
					 			errmsg,
					 			EXPECT_INTEGER,
					 			argidx
					 		);
							free(bld.buf);
							return NULL;
						}

						args.precision = intvalue(precptr);
					} else {
						const int *precptr = getarg_raw(argv, &argidx);
						args.precision = *precptr;
					}
				}
			}

			args.spec = *s++;

			/* check argc if the caller wants us to do so */
			if (argc >= 0 && argidx >= argc) {
				format_errmsg(errmsg, OUT_OF_ARGUMENTS, argidx);
				free(bld.buf);
				return NULL;
			}

			/* append parsed format string */
			if (append_format(&bld, &args, argv, &argidx, isval, errmsg) != 0) {
				free(bld.buf);
				return NULL;
			}

			/* update non-format chunk base pointer */
			p = s;
		} else {
			s++;
		}
	}

	/* if the format string doesn't end with a conversion specifier,
	 * then just append the last non-format (literal) string chunk
	 */
	if (s > p) {
		append_string(&bld, p, s - p);
	}

	/* append terminating NUL byte */
	expand_buffer(&bld, 1);
	bld.buf[bld.len] = 0;

	if (len != NULL) {
		*len = bld.len;
	}

	return bld.buf;
}
Example #21
0
/* returns zero on success, nonzero on error */
static int append_format(
	struct string_builder *bld,
	const struct format_args *args,
	void *argv,
	int *argidx,
	int isval,
	char **errmsg
)
{
	switch (args->spec) {
	case '%':
		append_string(bld, "%", 1);
		break;
	case 's': {
		const char *str;
		size_t len;

		if (isval) {
			SpnString *strobj;

			/* must be a string */
			SpnValue *val = getarg_val(argv, argidx);
			if (!isstring(val)) {
				format_errmsg(
					errmsg,
					TYPE_MISMATCH,
					*argidx,
					SPN_TYPE_STRING,
					val->type
				);
				return -1;
			}

			strobj = stringvalue(val);
			str = strobj->cstr;
			len = strobj->len;
		} else {
			str = getarg_raw(argv, argidx);
			len = strlen(str);
		}

		if (args->precision >= 0 && args->precision < len) {
			len = args->precision;
		}

		if (args->width >= 0 && args->width > len) {
			size_t pad = args->width - len;
			expand_buffer(bld, pad);

			while (pad-- > 0) {
				bld->buf[bld->len++] = ' ';
			}
		}

		append_string(bld, str, len);
		break;
	}
	case 'i':
	case 'd':
	case 'b':
	case 'o':
	case 'u':
	case 'x':
	case 'X': {
		char *buf, *end, *begin;
		size_t len = PR_LONG_DIGITS;
		enum format_flags flags = args->flags;
		unsigned base = base_for_specifier(args->spec);
		long n;
		unsigned long u;

		if (isval) {
			/* must be a number */
			SpnValue *val = getarg_val(argv, argidx);
			if (!isnum(val)) {
				format_errmsg(
					errmsg,
					TYPE_MISMATCH,
					*argidx,
					SPN_TTAG_NUMBER,
					val->type
				);
				return -1;
			}

			if (isint(val)) {
				n = intvalue(val);
			} else {
				n = floatvalue(val); /* truncate */
			}
		} else {
			/* "%i" expects an int, others expect a long */
			if (args->spec == 'i') {
				n = *(const int *)getarg_raw(argv, argidx);
			} else {
				n = *(const long *)getarg_raw(argv, argidx);
			}
		}

		if (args->spec == 'i' || args->spec == 'd') {
			/* signed conversion specifiers */
			if (n < 0) {
				flags |= FLAG_NEGATIVE;
				u = -n;
			} else {
				u = n;
			}
		} else {
			/* unsigned conversion specifiers */
			u = n;
		}

		if (args->spec == 'X') {
			flags |= FLAG_CAPS;
		}

		if (args->width >= 0 && args->width > len) {
			len = args->width;
		}

		buf = spn_malloc(len);
		end = buf + len;
		begin = ulong2str(end, u, base, args->width, flags);

		assert(buf <= begin);
		append_string(bld, begin, end - begin);
		free(buf);

		break;
	}
	case 'c': {
		unsigned char ch;
		int len = 1; /* one character is one character long... */

		if (isval) {
			/* must be an integer */
			SpnValue *val = getarg_val(argv, argidx);

			if (!isnum(val)) {
				format_errmsg(
					errmsg,
					TYPE_MISMATCH,
					*argidx,
					SPN_TTAG_NUMBER,
					val->type
				);
				return -1;
			}

			if (isfloat(val)) {
				format_errmsg(errmsg, EXPECT_INTEGER, *argidx);
				return -1;
			}

			ch = intvalue(val);
		} else {
			ch = *(const long *)getarg_raw(argv, argidx);
		}

		if (args->width > len) {
			len = args->width;
		}

		expand_buffer(bld, len);

		while (len-- > 1) {
			bld->buf[bld->len++] = ' ';
		}

		bld->buf[bld->len++] = ch;

		break;
	}
	case 'f':
	case 'F': {
		char *buf, *end, *begin;
		size_t len;
		int prec;
		double x;
		enum format_flags flags = args->flags;

		if (isval) {
			SpnValue *val = getarg_val(argv, argidx);
			if (!isnum(val)) {
				format_errmsg(
					errmsg,
					TYPE_MISMATCH,
					*argidx,
					SPN_TTAG_NUMBER,
					val->type
				);
				return -1;
			}

			if (isfloat(val)) {
				x = floatvalue(val);
			} else {
				x = intvalue(val);
			}
		} else {
			x = *(const double *)getarg_raw(argv, argidx);
		}

		if (args->spec == 'F') {
			flags |= FLAG_CAPS;
		}

		/* handle special cases */
		if (+1.0 / x == +1.0 / -0.0) {
			/* negative zero: set sign flag and carry on */
			flags |= FLAG_NEGATIVE;
		} else if (
			x != x		/*  NaN */
		     || x == +1.0 / 0.0	/* +inf */
		     || x == -1.0 / 0.0	/* -inf */
		) {
			print_special_fp(bld, flags, args->width, x);
			break;
		}

		if (x < 0.0) {
			flags |= FLAG_NEGATIVE;
			x = -x;
		}

		/* at this point, `x' is non-negative or -0 */

		if (x >= 1.0) {
			len = ceil(log10(x)) + 1; /* 10 ^ n is n + 1 digits long */
		} else {
			len = 1; /* leading zero needs exactly one character */
		}

		prec = args->precision < 0 ? DBL_DIG : args->precision;

		len += prec + 3; /* decimal point, sign, leading zero */

		if (args->width >= 0 && args->width > len) {
			len = args->width;
		}

		buf = spn_malloc(len);
		end = buf + len;
		begin = double2str(end, x, args->width, prec, flags);

		assert(buf <= begin);
		append_string(bld, begin, end - begin);
		free(buf);

		break;
	}
	case 'B': {
		int boolval;
		const char *str;
		size_t len;

		if (isval) {
			/* must be a boolean */
			SpnValue *val = getarg_val(argv, argidx);
			if (!isbool(val)) {
				format_errmsg(
					errmsg,
					TYPE_MISMATCH,
					*argidx,
					SPN_TTAG_BOOL,
					val->type
				);
				return -1;
			}

			boolval = boolvalue(val);
		} else {
			boolval = *(const int *)getarg_raw(argv, argidx);
		}

		str = boolval ? "true" : "false";
		len = strlen(str);

		if (args->precision >= 0 && args->precision < len) {
			len = args->precision;
		}

		if (args->width >= 0 && args->width > len) {
			size_t pad = args->width - len;
			expand_buffer(bld, pad);

			while (pad-- > 0) {
				bld->buf[bld->len++] = ' ';
			}
		}

		append_string(bld, str, len);
		break;
	}
	default:
		format_errmsg(errmsg, INVALID_SPECIFIER, ++*argidx, args->spec);
		return -1;
	}

	return 0;
}