155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 2001 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include <config.h>
3555682Smarkm
3655682Smarkm#include <stdio.h>
3755682Smarkm#include <ctype.h>
3855682Smarkm#include <string.h>
39178825Sdfr#include "roken.h"
4055682Smarkm#include "parse_units.h"
4155682Smarkm
4255682Smarkm/*
4355682Smarkm * Parse string in `s' according to `units' and return value.
4455682Smarkm * def_unit defines the default unit.
4555682Smarkm */
4655682Smarkm
4755682Smarkmstatic int
4855682Smarkmparse_something (const char *s, const struct units *units,
4955682Smarkm		 const char *def_unit,
5055682Smarkm		 int (*func)(int res, int val, unsigned mult),
5155682Smarkm		 int init,
5255682Smarkm		 int accept_no_val_p)
5355682Smarkm{
5455682Smarkm    const char *p;
5555682Smarkm    int res = init;
5655682Smarkm    unsigned def_mult = 1;
5755682Smarkm
5855682Smarkm    if (def_unit != NULL) {
5955682Smarkm	const struct units *u;
6055682Smarkm
6155682Smarkm	for (u = units; u->name; ++u) {
6255682Smarkm	    if (strcasecmp (u->name, def_unit) == 0) {
6355682Smarkm		def_mult = u->mult;
6455682Smarkm		break;
6555682Smarkm	    }
6655682Smarkm	}
6755682Smarkm	if (u->name == NULL)
6855682Smarkm	    return -1;
6955682Smarkm    }
7055682Smarkm
7155682Smarkm    p = s;
7255682Smarkm    while (*p) {
73233294Sstas	int val;
7455682Smarkm	char *next;
7555682Smarkm	const struct units *u, *partial_unit;
7655682Smarkm	size_t u_len;
7755682Smarkm	unsigned partial;
7855682Smarkm	int no_val_p = 0;
7955682Smarkm
8055682Smarkm	while(isspace((unsigned char)*p) || *p == ',')
8155682Smarkm	    ++p;
8255682Smarkm
83233294Sstas	val = strtol(p, &next, 0);
8478527Sassar	if (p == next) {
8578527Sassar	    val = 0;
8655682Smarkm	    if(!accept_no_val_p)
8755682Smarkm		return -1;
8855682Smarkm	    no_val_p = 1;
8955682Smarkm	}
9055682Smarkm	p = next;
9155682Smarkm	while (isspace((unsigned char)*p))
9255682Smarkm	    ++p;
9355682Smarkm	if (*p == '\0') {
9455682Smarkm	    res = (*func)(res, val, def_mult);
9555682Smarkm	    if (res < 0)
9655682Smarkm		return res;
9755682Smarkm	    break;
9855682Smarkm	} else if (*p == '+') {
9955682Smarkm	    ++p;
10055682Smarkm	    val = 1;
10155682Smarkm	} else if (*p == '-') {
10255682Smarkm	    ++p;
10355682Smarkm	    val = -1;
10455682Smarkm	}
10555682Smarkm	if (no_val_p && val == 0)
10655682Smarkm	    val = 1;
10755682Smarkm	u_len = strcspn (p, ", \t");
10855682Smarkm	partial = 0;
10955682Smarkm	partial_unit = NULL;
11055682Smarkm	if (u_len > 1 && p[u_len - 1] == 's')
11155682Smarkm	    --u_len;
11255682Smarkm	for (u = units; u->name; ++u) {
11355682Smarkm	    if (strncasecmp (p, u->name, u_len) == 0) {
11455682Smarkm		if (u_len == strlen (u->name)) {
11555682Smarkm		    p += u_len;
11655682Smarkm		    res = (*func)(res, val, u->mult);
11755682Smarkm		    if (res < 0)
11855682Smarkm			return res;
11955682Smarkm		    break;
12055682Smarkm		} else {
12155682Smarkm		    ++partial;
12255682Smarkm		    partial_unit = u;
12355682Smarkm		}
12455682Smarkm	    }
12555682Smarkm	}
12655682Smarkm	if (u->name == NULL) {
12755682Smarkm	    if (partial == 1) {
12855682Smarkm		p += u_len;
12955682Smarkm		res = (*func)(res, val, partial_unit->mult);
13055682Smarkm		if (res < 0)
13155682Smarkm		    return res;
13255682Smarkm	    } else {
13355682Smarkm		return -1;
13455682Smarkm	    }
13555682Smarkm	}
13655682Smarkm	if (*p == 's')
13755682Smarkm	    ++p;
13855682Smarkm    }
13955682Smarkm    return res;
14055682Smarkm}
14155682Smarkm
14255682Smarkm/*
14355682Smarkm * The string consists of a sequence of `n unit'
14455682Smarkm */
14555682Smarkm
14655682Smarkmstatic int
14755682Smarkmacc_units(int res, int val, unsigned mult)
14855682Smarkm{
14955682Smarkm    return res + val * mult;
15055682Smarkm}
15155682Smarkm
152233294SstasROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
15355682Smarkmparse_units (const char *s, const struct units *units,
15455682Smarkm	     const char *def_unit)
15555682Smarkm{
15655682Smarkm    return parse_something (s, units, def_unit, acc_units, 0, 0);
15755682Smarkm}
15855682Smarkm
15955682Smarkm/*
16055682Smarkm * The string consists of a sequence of `[+-]flag'.  `orig' consists
16155682Smarkm * the original set of flags, those are then modified and returned as
16255682Smarkm * the function value.
16355682Smarkm */
16455682Smarkm
16555682Smarkmstatic int
16655682Smarkmacc_flags(int res, int val, unsigned mult)
16755682Smarkm{
16855682Smarkm    if(val == 1)
16955682Smarkm	return res | mult;
17055682Smarkm    else if(val == -1)
17155682Smarkm	return res & ~mult;
17255682Smarkm    else if (val == 0)
17355682Smarkm	return mult;
17455682Smarkm    else
17555682Smarkm	return -1;
17655682Smarkm}
17755682Smarkm
178233294SstasROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
17955682Smarkmparse_flags (const char *s, const struct units *units,
18055682Smarkm	     int orig)
18155682Smarkm{
18255682Smarkm    return parse_something (s, units, NULL, acc_flags, orig, 1);
18355682Smarkm}
18455682Smarkm
18555682Smarkm/*
18655682Smarkm * Return a string representation according to `units' of `num' in `s'
18755682Smarkm * with maximum length `len'.  The actual length is the function value.
18855682Smarkm */
18955682Smarkm
19090926Snectarstatic int
19155682Smarkmunparse_something (int num, const struct units *units, char *s, size_t len,
192178825Sdfr		   int (*print) (char *, size_t, int, const char *, int),
193178825Sdfr		   int (*update) (int, unsigned),
19455682Smarkm		   const char *zero_string)
19555682Smarkm{
19655682Smarkm    const struct units *u;
19790926Snectar    int ret = 0, tmp;
19855682Smarkm
19955682Smarkm    if (num == 0)
20055682Smarkm	return snprintf (s, len, "%s", zero_string);
20155682Smarkm
20255682Smarkm    for (u = units; num > 0 && u->name; ++u) {
203178825Sdfr	int divisor;
20455682Smarkm
205178825Sdfr	divisor = num / u->mult;
206178825Sdfr	if (divisor) {
20755682Smarkm	    num = (*update) (num, u->mult);
208178825Sdfr	    tmp = (*print) (s, len, divisor, u->name, num);
20990926Snectar	    if (tmp < 0)
21090926Snectar		return tmp;
211233294Sstas	    if (tmp > (int) len) {
212178825Sdfr		len = 0;
213178825Sdfr		s = NULL;
214178825Sdfr	    } else {
215178825Sdfr		len -= tmp;
216178825Sdfr		s += tmp;
217178825Sdfr	    }
21855682Smarkm	    ret += tmp;
21955682Smarkm	}
22055682Smarkm    }
22155682Smarkm    return ret;
22255682Smarkm}
22355682Smarkm
22455682Smarkmstatic int
225178825Sdfrprint_unit (char *s, size_t len, int divisor, const char *name, int rem)
22655682Smarkm{
22755682Smarkm    return snprintf (s, len, "%u %s%s%s",
228178825Sdfr		     divisor, name,
229178825Sdfr		     divisor == 1 ? "" : "s",
23055682Smarkm		     rem > 0 ? " " : "");
23155682Smarkm}
23255682Smarkm
23355682Smarkmstatic int
23455682Smarkmupdate_unit (int in, unsigned mult)
23555682Smarkm{
23655682Smarkm    return in % mult;
23755682Smarkm}
23855682Smarkm
23955682Smarkmstatic int
24055682Smarkmupdate_unit_approx (int in, unsigned mult)
24155682Smarkm{
24255682Smarkm    if (in / mult > 0)
24355682Smarkm	return 0;
24455682Smarkm    else
24555682Smarkm	return update_unit (in, mult);
24655682Smarkm}
24755682Smarkm
248233294SstasROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
24955682Smarkmunparse_units (int num, const struct units *units, char *s, size_t len)
25055682Smarkm{
25155682Smarkm    return unparse_something (num, units, s, len,
25255682Smarkm			      print_unit,
25355682Smarkm			      update_unit,
25455682Smarkm			      "0");
25555682Smarkm}
25655682Smarkm
257233294SstasROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
25855682Smarkmunparse_units_approx (int num, const struct units *units, char *s, size_t len)
25955682Smarkm{
26055682Smarkm    return unparse_something (num, units, s, len,
26155682Smarkm			      print_unit,
26255682Smarkm			      update_unit_approx,
26355682Smarkm			      "0");
26455682Smarkm}
26555682Smarkm
266233294SstasROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
26755682Smarkmprint_units_table (const struct units *units, FILE *f)
26855682Smarkm{
26955682Smarkm    const struct units *u, *u2;
270233294Sstas    size_t max_sz = 0;
27155682Smarkm
27255682Smarkm    for (u = units; u->name; ++u) {
27355682Smarkm	max_sz = max(max_sz, strlen(u->name));
27455682Smarkm    }
27555682Smarkm
27655682Smarkm    for (u = units; u->name;) {
27755682Smarkm	char buf[1024];
27855682Smarkm	const struct units *next;
27955682Smarkm
28055682Smarkm	for (next = u + 1; next->name && next->mult == u->mult; ++next)
28155682Smarkm	    ;
28255682Smarkm
28355682Smarkm	if (next->name) {
28455682Smarkm	    for (u2 = next;
28555682Smarkm		 u2->name && u->mult % u2->mult != 0;
28655682Smarkm		 ++u2)
28755682Smarkm		;
28855682Smarkm	    if (u2->name == NULL)
28955682Smarkm		--u2;
29055682Smarkm	    unparse_units (u->mult, u2, buf, sizeof(buf));
291233294Sstas	    fprintf (f, "1 %*s = %s\n", (int)max_sz, u->name, buf);
29255682Smarkm	} else {
29355682Smarkm	    fprintf (f, "1 %s\n", u->name);
29455682Smarkm	}
29555682Smarkm	u = next;
29655682Smarkm    }
29755682Smarkm}
29855682Smarkm
29955682Smarkmstatic int
300178825Sdfrprint_flag (char *s, size_t len, int divisor, const char *name, int rem)
30155682Smarkm{
30255682Smarkm    return snprintf (s, len, "%s%s", name, rem > 0 ? ", " : "");
30355682Smarkm}
30455682Smarkm
30555682Smarkmstatic int
30655682Smarkmupdate_flag (int in, unsigned mult)
30755682Smarkm{
30855682Smarkm    return in - mult;
30955682Smarkm}
31055682Smarkm
311233294SstasROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
31255682Smarkmunparse_flags (int num, const struct units *units, char *s, size_t len)
31355682Smarkm{
31455682Smarkm    return unparse_something (num, units, s, len,
31555682Smarkm			      print_flag,
31655682Smarkm			      update_flag,
31755682Smarkm			      "");
31855682Smarkm}
31955682Smarkm
320233294SstasROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
32155682Smarkmprint_flags_table (const struct units *units, FILE *f)
32255682Smarkm{
32355682Smarkm    const struct units *u;
32455682Smarkm
32555682Smarkm    for(u = units; u->name; ++u)
32655682Smarkm	fprintf(f, "%s%s", u->name, (u+1)->name ? ", " : "\n");
32755682Smarkm}
328