1// Copyright 2014 The Android Open Source Project
2//
3// This software is licensed under the terms of the GNU General Public
4// License version 2, as published by the Free Software Foundation, and
5// may be copied, distributed, and modified under those terms.
6//
7// This program is distributed in the hope that it will be useful,
8// but WITHOUT ANY WARRANTY; without even the implied warranty of
9// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10// GNU General Public License for more details.
11
12#include "android/utils/property_file.h"
13
14#include "android/utils/system.h"
15
16#include <string.h>
17#include <stdlib.h>
18
19// Return true iff |ch| is whitespace. Don't use isspace() to avoid
20// locale-related results and associated issues.
21static int isspace(int ch) {
22    return (ch == ' ' || ch == '\t');
23}
24
25void propertyFileIterator_init(PropertyFileIterator* iter,
26                               const void* propFile,
27                               size_t propFileLen) {
28    iter->name[0] = '\0';
29    iter->value[0] = '\0';
30    iter->p = propFile;
31    iter->end = iter->p + propFileLen;
32}
33
34
35bool propertyFileIterator_next(PropertyFileIterator* iter) {
36    const char* p = iter->p;
37    const char* end = iter->end;
38    while (p < end) {
39        // Get end of line, and compute next line position.
40        const char* line = p;
41        const char* lineEnd = (const char*)memchr(p, '\n', end - p);
42        if (!lineEnd) {
43            lineEnd = end;
44            p = end;
45        } else {
46            p = lineEnd + 1;
47        }
48
49        // Remove trailing \r before the \n, if any.
50        if (lineEnd > line && lineEnd[-1] == '\r')
51            lineEnd--;
52
53        // Skip leading whitespace.
54        while (line < lineEnd && isspace(line[0]))
55            line++;
56
57        // Skip empty lines, and those that begin with '#' for comments.
58        if (lineEnd == line || line[0] == '#')
59            continue;
60
61        const char* name = line;
62        const char* nameEnd =
63                (const char*)memchr(name, '=', lineEnd - name);
64        if (!nameEnd) {
65            // Skipping lines without a =
66            continue;
67        }
68        const char* value = nameEnd + 1;
69        while (nameEnd > name && isspace(nameEnd[-1]))
70            nameEnd--;
71
72        size_t nameLen = nameEnd - name;
73        if (nameLen == 0 || nameLen >= MAX_PROPERTY_NAME_LEN) {
74            // Skip lines without names, or with names too long.
75            continue;
76        }
77
78        memcpy(iter->name, name, nameLen);
79        iter->name[nameLen] = '\0';
80
81        // Truncate value's length.
82        size_t valueLen = (lineEnd - value);
83        if (valueLen >= MAX_PROPERTY_VALUE_LEN)
84            valueLen = (MAX_PROPERTY_VALUE_LEN - 1);
85
86        memcpy(iter->value, value, valueLen);
87        iter->value[valueLen] = '\0';
88
89        iter->p = p;
90        return true;
91    }
92    iter->p = p;
93    return false;
94}
95
96char* propertyFile_getValue(const char* propFile,
97                            size_t propFileLen,
98                            const char* propName) {
99    size_t propNameLen = strlen(propName);
100    if (propNameLen >= MAX_PROPERTY_NAME_LEN)
101        return NULL;
102
103    char* ret = NULL;
104    PropertyFileIterator iter[1];
105    propertyFileIterator_init(iter, propFile, propFileLen);
106    while (propertyFileIterator_next(iter)) {
107        if (!strcmp(iter->name, propName)) {
108            free(ret);
109            ret = ASTRDUP(iter->value);
110        }
111    }
112    return ret;
113}
114