Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Source for lineendings plugin
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 :
11 : #ifndef HAVE_KDBCONFIG
12 : #include "kdbconfig.h"
13 : #endif
14 :
15 : #include "lineendings.h"
16 : #include <kdberrors.h>
17 : #include <stdio.h>
18 : #include <string.h>
19 : #include <unistd.h>
20 :
21 : #define LF_BYTE 0x0A
22 : #define CR_BYTE 0x0D
23 :
24 : typedef enum
25 : {
26 : NA,
27 : CR,
28 : LF,
29 : CRLF,
30 : LFCR,
31 : NUM_TYPES
32 : } Lineending;
33 :
34 : static inline char * LEString (Lineending index)
35 : {
36 : static char * strings[] = { "NA", "CR", "LF", "CRLF", "LFCR" };
37 56 : if (index > NUM_TYPES) return NULL;
38 56 : return strings[index];
39 : }
40 12 : static Lineending strToLE (const char * str)
41 : {
42 12 : uint8_t counter = 0;
43 64 : for (; counter < NUM_TYPES; ++counter)
44 : {
45 112 : if (!strcmp (LEString (counter), str)) return counter;
46 : }
47 : return NA;
48 : }
49 12 : static int checkLineEndings (const char * fileName, Lineending validLineEnding, Key * parentKey)
50 : {
51 : FILE * fp;
52 12 : fp = fopen (fileName, "rb");
53 12 : if (fp == NULL)
54 : {
55 : return -1;
56 : }
57 :
58 12 : Lineending lineEnding = NA;
59 12 : Lineending found = NA;
60 : uint8_t fc, sc;
61 12 : unsigned long line = 1;
62 12 : fc = sc = 0;
63 12 : (void) fread (&fc, 1, 1, fp);
64 132 : while (!feof (fp))
65 : {
66 116 : (void) fread (&sc, 1, 1, fp);
67 116 : switch (fc)
68 : {
69 : case LF_BYTE:
70 8 : if (sc == CR_BYTE)
71 : found = LFCR;
72 8 : else if (sc == LF_BYTE)
73 : found = LF;
74 4 : else if (sc)
75 4 : found = LF;
76 : break;
77 : case CR_BYTE:
78 12 : if (sc == LF_BYTE)
79 : found = CRLF;
80 0 : else if (sc == CR_BYTE)
81 : found = CR;
82 0 : else if (sc)
83 0 : found = CR;
84 : break;
85 : }
86 116 : if (found == CRLF || found == LFCR)
87 : {
88 12 : (void) fread (&sc, 1, 1, fp);
89 : }
90 116 : if (lineEnding == NA && found != NA)
91 : {
92 12 : lineEnding = found;
93 12 : if (validLineEnding != NA && lineEnding != validLineEnding)
94 : {
95 4 : fclose (fp);
96 4 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Invalid line ending at line %lu", line);
97 4 : return -2;
98 : }
99 8 : ++line;
100 : }
101 104 : else if (lineEnding != found && found != NA)
102 : {
103 4 : fclose (fp);
104 4 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Inconsistent line endings at line %lu", line);
105 4 : return -3;
106 : }
107 108 : fc = sc;
108 108 : found = NA;
109 : }
110 4 : fclose (fp);
111 4 : return 0;
112 : }
113 :
114 26 : int elektraLineendingsGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
115 : {
116 26 : if (!strcmp (keyName (parentKey), "system/elektra/modules/lineendings"))
117 : {
118 20 : KeySet * contract = ksNew (
119 : 30, keyNew ("system/elektra/modules/lineendings", KEY_VALUE, "lineendings plugin waits for your orders", KEY_END),
120 : keyNew ("system/elektra/modules/lineendings/exports", KEY_END),
121 : keyNew ("system/elektra/modules/lineendings/exports/get", KEY_FUNC, elektraLineendingsGet, KEY_END),
122 : keyNew ("system/elektra/modules/lineendings/exports/set", KEY_FUNC, elektraLineendingsSet, KEY_END),
123 : #include ELEKTRA_README
124 : keyNew ("system/elektra/modules/lineendings/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
125 20 : ksAppend (returned, contract);
126 20 : ksDel (contract);
127 :
128 20 : return 1; /* success */
129 : }
130 : /* get all keys */
131 6 : KeySet * config = elektraPluginGetConfig (handle);
132 6 : Key * valid = ksLookupByName (config, "/valid", 0);
133 6 : Lineending validLineEnding = strToLE (keyString (valid));
134 : int ret;
135 6 : ret = checkLineEndings (keyString (parentKey), validLineEnding, parentKey);
136 6 : if (ret == (-3))
137 : {
138 : return -1;
139 : }
140 : else
141 4 : return 1;
142 : }
143 :
144 6 : int elektraLineendingsSet (Plugin * handle, KeySet * returned ELEKTRA_UNUSED, Key * parentKey)
145 : {
146 6 : KeySet * config = elektraPluginGetConfig (handle);
147 6 : Key * valid = ksLookupByName (config, "/valid", 0);
148 6 : Lineending validLineEnding = strToLE (keyString (valid));
149 : int ret;
150 6 : ret = checkLineEndings (keyString (parentKey), validLineEnding, parentKey);
151 6 : switch (ret)
152 : {
153 : case (-1):
154 0 : ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Couldn't open file %s\n", keyString (parentKey));
155 0 : return 1;
156 : break;
157 : case (-2):
158 : return -1;
159 : break;
160 : case (-3):
161 : return -1;
162 : break;
163 : case 0:
164 : default:
165 2 : return 1;
166 : break;
167 : }
168 : }
169 :
170 26 : Plugin * ELEKTRA_PLUGIN_EXPORT
171 : {
172 : // clang-format off
173 26 : return elektraPluginExport("lineendings",
174 : ELEKTRA_PLUGIN_GET, &elektraLineendingsGet,
175 : ELEKTRA_PLUGIN_SET, &elektraLineendingsSet,
176 : // ELEKTRA_PLUGIN_ERROR, &elektraLineendingsError,
177 : ELEKTRA_PLUGIN_END);
178 : }
179 :
|