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