1/******************************************************************************
2 *   Copyright (C) 2009-2015, 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 = NULL;
25    char* tmpFlagBuffer = NULL;
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        goto parseFlagsFile_cleanup;
34    }
35
36    buffer = uprv_malloc(sizeof(char) * currentBufferSize);
37    tmpFlagBuffer = uprv_malloc(sizeof(char) * flagBufferSize);
38
39    if (buffer == NULL || tmpFlagBuffer == NULL) {
40        *status = U_MEMORY_ALLOCATION_ERROR;
41        goto parseFlagsFile_cleanup;
42    }
43
44    do {
45        if (allocateMoreSpace) {
46            allocateMoreSpace = FALSE;
47            currentBufferSize *= 2;
48            uprv_free(buffer);
49            buffer = uprv_malloc(sizeof(char) * currentBufferSize);
50            if (buffer == NULL) {
51                *status = U_MEMORY_ALLOCATION_ERROR;
52                goto parseFlagsFile_cleanup;
53            }
54        }
55        for (i = 0; i < numOfFlags;) {
56            if (T_FileStream_readLine(f, buffer, currentBufferSize) == NULL) {
57                /* End of file reached. */
58                break;
59            }
60            if (buffer[0] == '#') {
61                continue;
62            }
63
64            if (uprv_strlen(buffer) == (currentBufferSize - 1) && buffer[currentBufferSize-2] != '\n') {
65                /* Allocate more space for buffer if it didnot read the entrire line */
66                allocateMoreSpace = TRUE;
67                T_FileStream_rewind(f);
68                break;
69            } else {
70                idx = extractFlag(buffer, currentBufferSize, tmpFlagBuffer, flagBufferSize, flagNames, numOfFlags, status);
71                if (U_FAILURE(*status)) {
72                    if (*status == U_BUFFER_OVERFLOW_ERROR) {
73                        result = currentBufferSize;
74                    } else {
75                        result = -1;
76                    }
77                    break;
78                } else {
79                    if (flagNames != NULL) {
80                        if (idx >= 0) {
81                            uprv_strcpy(flagBuffer[idx], tmpFlagBuffer);
82                        } else {
83                            /* No match found.  Skip it. */
84                            continue;
85                        }
86                    } else {
87                        uprv_strcpy(flagBuffer[i++], tmpFlagBuffer);
88                    }
89                }
90            }
91        }
92    } while (allocateMoreSpace && U_SUCCESS(*status));
93
94parseFlagsFile_cleanup:
95    uprv_free(tmpFlagBuffer);
96    uprv_free(buffer);
97
98    T_FileStream_close(f);
99
100    if (U_FAILURE(*status)) {
101        return -1;
102    }
103
104    if (U_SUCCESS(*status) && result == 0) {
105        currentBufferSize = DEFAULT_BUFFER_SIZE;
106    }
107
108    return result;
109}
110
111
112/*
113 * Extract the setting after the '=' and store it in flag excluding the newline character.
114 */
115static int32_t extractFlag(char* buffer, int32_t bufferSize, char* flag, int32_t flagSize, const char **flagNames, int32_t numOfFlags, UErrorCode *status) {
116    int32_t i, idx = -1;
117    char *pBuffer;
118    int32_t offset=0;
119    UBool bufferWritten = FALSE;
120
121    if (buffer[0] != 0) {
122        /* Get the offset (i.e. position after the '=') */
123        offset = getFlagOffset(buffer, bufferSize);
124        pBuffer = buffer+offset;
125        for(i = 0;;i++) {
126            if (i >= flagSize) {
127                *status = U_BUFFER_OVERFLOW_ERROR;
128                return -1;
129            }
130            if (pBuffer[i+1] == 0) {
131                /* Indicates a new line character. End here. */
132                flag[i] = 0;
133                break;
134            }
135
136            flag[i] = pBuffer[i];
137            if (i == 0) {
138                bufferWritten = TRUE;
139            }
140        }
141    }
142
143    if (!bufferWritten) {
144        flag[0] = 0;
145    }
146
147    if (flagNames != NULL && offset>0) {
148        offset--;  /* Move offset back 1 because of '='*/
149        for (i = 0; i < numOfFlags; i++) {
150            if (uprv_strncmp(buffer, flagNames[i], offset) == 0) {
151                idx = i;
152                break;
153            }
154        }
155    }
156
157    return idx;
158}
159
160/*
161 * Get the position after the '=' character.
162 */
163static int32_t getFlagOffset(const char *buffer, int32_t bufferSize) {
164    int32_t offset = 0;
165
166    for (offset = 0; offset < bufferSize;offset++) {
167        if (buffer[offset] == '=') {
168            offset++;
169            break;
170        }
171    }
172
173    if (offset == bufferSize || (offset - 1) == bufferSize) {
174        offset = 0;
175    }
176
177    return offset;
178}
179