|  | /* | 
|  | *  Copyright (C) 2010-2011, Parallels, Inc. All rights reserved. | 
|  | * | 
|  | *  This program is free software; you can redistribute it and/or modify | 
|  | *  it under the terms of the GNU General Public License as published by | 
|  | *  the Free Software Foundation; either version 2 of the License, or | 
|  | *  (at your option) any later version. | 
|  | * | 
|  | *  This program is distributed in the hope that it will be useful, | 
|  | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | *  GNU General Public License for more details. | 
|  | * | 
|  | *  You should have received a copy of the GNU General Public License | 
|  | *  along with this program; if not, write to the Free Software | 
|  | *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | */ | 
|  |  | 
|  | #include <ctype.h> | 
|  | #include <errno.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  |  | 
|  | #include "bitmap.h" | 
|  |  | 
|  | static int bitmap_find_first_bit(const unsigned long *maskp, int nmaskbits) | 
|  | { | 
|  | int i, n; | 
|  | int nmasklongs = BITS_TO_LONGS(nmaskbits); | 
|  |  | 
|  | for (i = 0; i < nmasklongs; i++) { | 
|  | if (maskp[i] != 0) | 
|  | break; | 
|  | } | 
|  | if (i == nmasklongs) | 
|  | return nmaskbits; | 
|  | i *= BITS_PER_LONG; | 
|  | n = i + BITS_PER_LONG; | 
|  | n = MIN(n, nmaskbits); | 
|  | do { | 
|  | if (bitmap_test_bit(i, maskp)) | 
|  | break; | 
|  | } while (++i < n); | 
|  | return i; | 
|  | } | 
|  |  | 
|  | static int bitmap_find_next_bit(const unsigned long *maskp, int nmaskbits, int offset) | 
|  | { | 
|  | int n; | 
|  |  | 
|  | if (offset % BITS_PER_LONG != 0) { | 
|  | n = (BIT_WORD(offset) + 1) * BITS_PER_LONG; | 
|  | n = MIN(n, nmaskbits); | 
|  | while (offset < n) { | 
|  | if (bitmap_test_bit(offset, maskp)) | 
|  | return offset; | 
|  | offset++; | 
|  | } | 
|  | } | 
|  | if (offset >= nmaskbits) | 
|  | return nmaskbits; | 
|  | return offset + bitmap_find_first_bit( | 
|  | maskp + BIT_WORD(offset), nmaskbits - offset); | 
|  | } | 
|  |  | 
|  | int bitmap_find_first_zero_bit(const unsigned long *maskp, int nmaskbits) | 
|  | { | 
|  | int i, n; | 
|  | int nmasklongs = BITS_TO_LONGS(nmaskbits); | 
|  |  | 
|  | for (i = 0; i < nmasklongs; i++) { | 
|  | if (~maskp[i] != 0) | 
|  | break; | 
|  | } | 
|  | if (i == nmasklongs) | 
|  | return nmaskbits; | 
|  | i *= BITS_PER_LONG; | 
|  | n = i + BITS_PER_LONG; | 
|  | n = MIN(n, nmaskbits); | 
|  | do { | 
|  | if (!bitmap_test_bit(i, maskp)) | 
|  | break; | 
|  | } while (++i < n); | 
|  | return i; | 
|  | } | 
|  |  | 
|  | static int bitmap_find_next_zero_bit(const unsigned long *maskp, | 
|  | int nmaskbits, int offset) | 
|  | { | 
|  | int n; | 
|  |  | 
|  | if (offset % BITS_PER_LONG != 0) { | 
|  | n = (BIT_WORD(offset) + 1) * BITS_PER_LONG; | 
|  | n = MIN(n, nmaskbits); | 
|  | while (offset < n) { | 
|  | if (!bitmap_test_bit(offset, maskp)) | 
|  | return offset; | 
|  | offset++; | 
|  | } | 
|  | } | 
|  | if (offset >= nmaskbits) | 
|  | return nmaskbits; | 
|  | return offset + bitmap_find_first_zero_bit( | 
|  | maskp + BIT_WORD(offset), nmaskbits - offset); | 
|  | } | 
|  |  | 
|  | static inline int parse_range(const char *str, int *a, int *b, char **endptr) | 
|  | { | 
|  | if (!isdigit(*str)) | 
|  | return -1; | 
|  | *a = *b = strtol(str, endptr, 10); | 
|  | if (**endptr == '-') { | 
|  | str = *endptr + 1; | 
|  | if (!isdigit(*str)) | 
|  | return -1; | 
|  | *b = strtol(str, endptr, 10); | 
|  | if (*a > *b) | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * bitmap_parse - parse bitmap stored as a comma-separated list of decimal | 
|  | * numbers and ranges in the string @str and write it to the bitmap @maskp | 
|  | * containing at most @nmaskbits. | 
|  | * A range of bits is shown as two hyphen-separated decimal numbers with | 
|  | * the first number being the smallest and the second one being the largest | 
|  | * bit numbers set in the range. | 
|  | * | 
|  | * Returns 0 on success. On invalid input strings, -1 is returned, | 
|  | * and @errno is set appropriately. | 
|  | * Errors: | 
|  | * %EINVAL: invalid character in string or | 
|  | *          second number in range smaller than first | 
|  | * %ERANGE: bit number specified too large for mask | 
|  | */ | 
|  | int bitmap_parse(const char *str, unsigned long *maskp, int nmaskbits) | 
|  | { | 
|  | int a, b; | 
|  | char *endptr; | 
|  |  | 
|  | bitmap_zero(maskp, nmaskbits); | 
|  | do { | 
|  | if (parse_range(str, &a, &b, &endptr) != 0) { | 
|  | errno = EINVAL; | 
|  | return -1; | 
|  | } | 
|  | if (b >= nmaskbits) { | 
|  | errno = ERANGE; | 
|  | return -1; | 
|  | } | 
|  | for (; a <= b; a++) | 
|  | bitmap_set_bit(a, maskp); | 
|  | if (*endptr == ',') | 
|  | endptr++; | 
|  | str = endptr; | 
|  | } while (*str != '\0'); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static inline int print_range(char *buf, unsigned int buflen, int a, int b) | 
|  | { | 
|  | if (a == b) | 
|  | return snprintf(buf, buflen, "%d", a); | 
|  | return snprintf(buf, buflen, "%d-%d", a, b); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * bitmap_snprintf - convert bitmap to string. | 
|  | * @buf: buffer to store string | 
|  | * @buflen: size of @buf, in bytes | 
|  | * @maskp: bitmap to convert | 
|  | * @nmaskbits: size of bitmap, in bits | 
|  | * | 
|  | * For description of output format, see bitmap_parse(). | 
|  | * | 
|  | * Returns the number of characters printed, excluding the trailing '\0'. | 
|  | * If the output was truncated due to the limit on the size of @buf than | 
|  | * the return value is the number of characters which would have been written | 
|  | * to the final string if enough space had been available. | 
|  | */ | 
|  | int bitmap_snprintf(char *buf, unsigned int buflen, | 
|  | const unsigned long *maskp, int nmaskbits) | 
|  | { | 
|  | int a, b; | 
|  | unsigned int len = 0; | 
|  |  | 
|  | if (buflen > 0) | 
|  | buf[0] = '\0'; | 
|  | a = bitmap_find_first_bit(maskp, nmaskbits); | 
|  | while (a < nmaskbits) { | 
|  | b = bitmap_find_next_zero_bit(maskp, nmaskbits, a + 1) - 1; | 
|  | if (len > 0) | 
|  | len += snprintf(buf + len, | 
|  | buflen > len ? buflen - len : 0, ","); | 
|  | len += print_range(buf + len, | 
|  | buflen > len ? buflen - len : 0, a, b); | 
|  | a = bitmap_find_next_bit(maskp, nmaskbits, b + 1); | 
|  | } | 
|  | return len; | 
|  | } |