1/******************************************************************************
2 *   Copyright (C) 2009-2012, International Business Machines
3 *   Corporation and others.  All Rights Reserved.
4 *******************************************************************************
5 */
6
7#include "flagparser.h"
8#include "filestrm.h"
9#include "cstring.h"
10#include "cmemory.h"
11
12#define DEFAULT_BUFFER_SIZE 512
13
14static int32_t currentBufferSize = DEFAULT_BUFFER_SIZE;
15
16static int32_t extractFlag(char* buffer, int32_t bufferSize, char* flag, int32_t flagSize, const char ** flagNames, int32_t numOfFlags, UErrorCode *status);
17static int32_t getFlagOffset(const char *buffer, int32_t bufferSize);
18
19/*
20 * Opens the given fileName and reads in the information storing the data in flagBuffer.
21 */
22U_CAPI int32_t U_EXPORT2
23parseFlagsFile(const char *fileName, char **flagBuffer, int32_t flagBufferSize, const char ** flagNames, int32_t numOfFlags, UErrorCode *status) {
24    char* buffer = uprv_malloc(sizeof(char) * currentBufferSize);
25    char* tmpFlagBuffer = uprv_malloc(sizeof(char) * flagBufferSize);
26    UBool allocateMoreSpace = FALSE;
27    int32_t idx, i;
28    int32_t result = 0;
29
30    FileStream *f = T_FileStream_open(fileName, "r");
31    if (f == NULL) {
32        *status = U_FILE_ACCESS_ERROR;
33        return -1;
34    }
35
36    if (buffer == NULL) {
37        *status = U_MEMORY_ALLOCATION_ERROR;
38        return -1;
39    }
40
41    do {
42        if (allocateMoreSpace) {
43            allocateMoreSpace = FALSE;
44            currentBufferSize *= 2;
45            uprv_free(buffer);
46            buffer = uprv_malloc(sizeof(char) * currentBufferSize);
47            if (buffer == NULL) {
48                uprv_free(tmpFlagBuffer);
49                *status = U_MEMORY_ALLOCATION_ERROR;
50                return -1;
51            }
52        }
53        for (i = 0; i < numOfFlags;) {
54            if (T_FileStream_readLine(f, buffer, currentBufferSize) == NULL) {
55                /* End of file reached. */
56                break;
57            }
58            if (buffer[0] == '#') {
59                continue;
60            }
61
62            if (uprv_strlen(buffer) == (currentBufferSize - 1) && buffer[currentBufferSize-2] != '\n') {
63                /* Allocate more space for buffer if it didnot read the entrire line */
64                allocateMoreSpace = TRUE;
65                T_FileStream_rewind(f);
66                break;
67            } else {
68                idx = extractFlag(buffer, currentBufferSize, tmpFlagBuffer, flagBufferSize, flagNames, numOfFlags, status);
69                if (U_FAILURE(*status)) {
70                    if (*status == U_BUFFER_OVERFLOW_ERROR) {
71                        result = currentBufferSize;
72                    } else {
73                        result = -1;
74                    }
75                    break;
76                } else {
77                    if (flagNames != NULL) {
78                        if (idx >= 0) {
79                            uprv_strcpy(flagBuffer[idx], tmpFlagBuffer);
80                        } else {
81                            /* No match found.  Skip it. */
82                            continue;
83                        }
84                    } else {
85                        uprv_strcpy(flagBuffer[i++], tmpFlagBuffer);
86                    }
87                }
88            }
89        }
90    } while (allocateMoreSpace && U_SUCCESS(*status));
91
92    uprv_free(tmpFlagBuffer);
93    uprv_free(buffer);
94
95    T_FileStream_close(f);
96
97    if (U_SUCCESS(*status) && result == 0) {
98        currentBufferSize = DEFAULT_BUFFER_SIZE;
99    }
100
101    return result;
102}
103
104
105/*
106 * Extract the setting after the '=' and store it in flag excluding the newline character.
107 */
108static int32_t extractFlag(char* buffer, int32_t bufferSize, char* flag, int32_t flagSize, const char **flagNames, int32_t numOfFlags, UErrorCode *status) {
109    int32_t i, idx = -1;
110    char *pBuffer;
111    int32_t offset=0;
112    UBool bufferWritten = FALSE;
113
114    if (buffer[0] != 0) {
115        /* Get the offset (i.e. position after the '=') */
116        offset = getFlagOffset(buffer, bufferSize);
117        pBuffer = buffer+offset;
118        for(i = 0;;i++) {
119            if (i >= flagSize) {
120                *status = U_BUFFER_OVERFLOW_ERROR;
121                return -1;
122            }
123            if (pBuffer[i+1] == 0) {
124                /* Indicates a new line character. End here. */
125                flag[i] = 0;
126                break;
127            }
128
129            flag[i] = pBuffer[i];
130            if (i == 0) {
131                bufferWritten = TRUE;
132            }
133        }
134    }
135
136    if (!bufferWritten) {
137        flag[0] = 0;
138    }
139
140    if (flagNames != NULL && offset>0) {
141        offset--;  /* Move offset back 1 because of '='*/
142        for (i = 0; i < numOfFlags; i++) {
143            if (uprv_strncmp(buffer, flagNames[i], offset) == 0) {
144                idx = i;
145                break;
146            }
147        }
148    }
149
150    return idx;
151}
152
153/*
154 * Get the position after the '=' character.
155 */
156static int32_t getFlagOffset(const char *buffer, int32_t bufferSize) {
157    int32_t offset = 0;
158
159    for (offset = 0; offset < bufferSize;offset++) {
160        if (buffer[offset] == '=') {
161            offset++;
162            break;
163        }
164    }
165
166    if (offset == bufferSize || (offset - 1) == bufferSize) {
167        offset = 0;
168    }
169
170    return offset;
171}
172