1 /*
2 OWFS -- One-Wire filesystem
3 OWHTTPD -- One-Wire Web Server
4 Written 2003 Paul H Alfille
5 email: paul.alfille@gmail.com
6 Released under the GPL
7 See the header file: ow.h for full attribution
8 1wire/iButton system from Dallas Semiconductor
9 $ID: $
10 */
11
12 #include <config.h>
13 #include "owfs_config.h"
14 #include "ow.h"
15 #include "ow_counters.h"
16 #include "ow_connection.h"
17
18 /* ------- Prototypes ----------- */
19 static SIZE_OR_ERROR OWQ_parse_output_integer(struct one_wire_query *owq);
20 static SIZE_OR_ERROR OWQ_parse_output_unsigned(struct one_wire_query *owq);
21 static SIZE_OR_ERROR OWQ_parse_output_float(struct one_wire_query *owq);
22 static SIZE_OR_ERROR OWQ_parse_output_date(struct one_wire_query *owq);
23 static SIZE_OR_ERROR OWQ_parse_output_yesno(struct one_wire_query *owq);
24 static SIZE_OR_ERROR OWQ_parse_output_ascii(struct one_wire_query *owq);
25 static SIZE_OR_ERROR OWQ_parse_output_array_with_commas(struct one_wire_query *owq);
26 static SIZE_OR_ERROR OWQ_parse_output_array_no_commas(struct one_wire_query *owq);
27 static SIZE_OR_ERROR OWQ_parse_output_ascii_array(struct one_wire_query *owq);
28 static SIZE_OR_ERROR OWQ_parse_output_offset_and_size_z(const char *string, struct one_wire_query *owq) ;
29 static SIZE_OR_ERROR OWQ_parse_output_offset_and_size(const char *string, size_t length, struct one_wire_query *owq) ;
30
31 /*
32 Change in strategy 6/2006:
33 Now use CheckPresence as primary method of finding correct bus
34 Can break down cases into:
35 1. bad ParsedName -- no read possible
36 2. structure -- read from 1st bus (local)
37 3. specified bus (picked up in ParsedName) -- use that
38 4. statistics, settings, Simultaneous, Thermostat -- use first or specified
39 5. real -- use caced, if error, delete cache entry and try twice more.
40 */
41
42 /* ---------------------------------------------- */
43 /* Filesystem callback functions */
44 /* ---------------------------------------------- */
45
OWQ_parse_output(struct one_wire_query * owq)46 SIZE_OR_ERROR OWQ_parse_output(struct one_wire_query *owq)
47 {
48 // have to check if offset is beyond the filesize.
49 if (OWQ_offset(owq)) {
50 size_t file_length = 0;
51 file_length = FileLength(PN(owq));
52 LEVEL_DEBUG("file_length=%lu offset=%lu size=%lu",
53 (unsigned long) file_length, (unsigned long) OWQ_offset(owq), (unsigned long) OWQ_size(owq));
54 if ((unsigned long) OWQ_offset(owq) >= (unsigned long) file_length) {
55 return 0; // This is data-length
56 }
57 }
58
59 /* Special case, structure is always ascii */
60 if (OWQ_pn(owq).type == ePN_structure) {
61 return OWQ_parse_output_ascii(owq);
62 }
63
64 switch (OWQ_pn(owq).extension) {
65 case EXTENSION_BYTE:
66 return OWQ_parse_output_unsigned(owq);
67 case EXTENSION_ALL:
68 switch (OWQ_pn(owq).selected_filetype->format) {
69 case ft_ascii:
70 case ft_vascii:
71 case ft_alias:
72 return OWQ_parse_output_ascii_array(owq);
73 case ft_binary:
74 return OWQ_parse_output_array_no_commas(owq);
75 default:
76 return OWQ_parse_output_array_with_commas(owq);
77 }
78 default:
79 switch (OWQ_pn(owq).selected_filetype->format) {
80 case ft_integer:
81 return OWQ_parse_output_integer(owq);
82 case ft_yesno:
83 case ft_bitfield:
84 return OWQ_parse_output_yesno(owq);
85 case ft_unsigned:
86 return OWQ_parse_output_unsigned(owq);
87 case ft_pressure:
88 case ft_temperature:
89 case ft_tempgap:
90 case ft_float:
91 return OWQ_parse_output_float(owq);
92 case ft_date:
93 return OWQ_parse_output_date(owq);
94 case ft_vascii:
95 case ft_alias:
96 case ft_ascii:
97 case ft_binary:
98 return OWQ_parse_output_ascii(owq);
99 case ft_directory:
100 case ft_subdir:
101 case ft_unknown:
102 return -ENOENT;
103 }
104 }
105 return -EINVAL; // should never be reached if all the cases are truly covered
106 }
107
OWQ_parse_output_integer(struct one_wire_query * owq)108 static SIZE_OR_ERROR OWQ_parse_output_integer(struct one_wire_query *owq)
109 {
110 /* should only need suglen+1, but uClibc's snprintf()
111 seem to trash 'len' if not increased */
112 int len;
113 char c[PROPERTY_LENGTH_INTEGER + 2];
114
115 UCLIBCLOCK;
116 if ( ShouldTrim(PN(owq)) ) {
117 len = snprintf(c, PROPERTY_LENGTH_INTEGER + 1, "%1d", OWQ_I(owq));
118 } else {
119 len = snprintf(c, PROPERTY_LENGTH_INTEGER + 1, "%*d", PROPERTY_LENGTH_INTEGER, OWQ_I(owq));
120 }
121 UCLIBCUNLOCK;
122 if ((len < 0) || ((size_t) len > PROPERTY_LENGTH_INTEGER)) {
123 return -EMSGSIZE;
124 }
125 return OWQ_parse_output_offset_and_size(c, len, owq);
126 }
127
OWQ_parse_output_unsigned(struct one_wire_query * owq)128 static SIZE_OR_ERROR OWQ_parse_output_unsigned(struct one_wire_query *owq)
129 {
130 /* should only need suglen+1, but uClibc's snprintf()
131 seem to trash 'len' if not increased */
132 int len;
133 char c[PROPERTY_LENGTH_UNSIGNED + 2];
134
135 UCLIBCLOCK;
136 if ( ShouldTrim(PN(owq)) ) {
137 len = snprintf(c, PROPERTY_LENGTH_UNSIGNED + 1, "%1u", OWQ_U(owq));
138 } else {
139 len = snprintf(c, PROPERTY_LENGTH_UNSIGNED + 1, "%*u", PROPERTY_LENGTH_UNSIGNED, OWQ_U(owq));
140 }
141 UCLIBCUNLOCK;
142 if ((len < 0) || ((size_t) len > PROPERTY_LENGTH_UNSIGNED)) {
143 return -EMSGSIZE;
144 }
145 return OWQ_parse_output_offset_and_size(c, len, owq);
146 }
147
OWQ_parse_output_float(struct one_wire_query * owq)148 static SIZE_OR_ERROR OWQ_parse_output_float(struct one_wire_query *owq)
149 {
150 /* should only need suglen+1, but uClibc's snprintf()
151 seem to trash 'len' if not increased */
152 int len;
153 char c[PROPERTY_LENGTH_FLOAT + 2];
154 _FLOAT F;
155
156 switch (OWQ_pn(owq).selected_filetype->format) {
157 case ft_pressure:
158 F = Pressure(OWQ_F(owq), PN(owq));
159 break;
160 case ft_temperature:
161 F = Temperature(OWQ_F(owq), PN(owq));
162 break;
163 case ft_tempgap:
164 F = TemperatureGap(OWQ_F(owq), PN(owq));
165 break;
166 default:
167 F = OWQ_F(owq);
168 break;
169 }
170
171 UCLIBCLOCK;
172 if ( ShouldTrim(PN(owq)) ) {
173 len = snprintf(c, PROPERTY_LENGTH_FLOAT + 1, "%1G", F);
174 } else {
175 len = snprintf(c, PROPERTY_LENGTH_FLOAT + 1, "%*G", PROPERTY_LENGTH_FLOAT, F);
176 }
177 UCLIBCUNLOCK;
178 if ((len < 0) || ((size_t) len > PROPERTY_LENGTH_FLOAT)) {
179 return -EMSGSIZE;
180 }
181 return OWQ_parse_output_offset_and_size(c, len, owq);
182 }
183
OWQ_parse_output_date(struct one_wire_query * owq)184 static SIZE_OR_ERROR OWQ_parse_output_date(struct one_wire_query *owq)
185 {
186 char c[PROPERTY_LENGTH_DATE + 2];
187 if (OWQ_size(owq) < PROPERTY_LENGTH_DATE) {
188 return -EMSGSIZE;
189 }
190 ctime_r(&OWQ_D(owq), c);
191 return OWQ_parse_output_offset_and_size(c, PROPERTY_LENGTH_DATE, owq);
192 }
193
OWQ_parse_output_yesno(struct one_wire_query * owq)194 static SIZE_OR_ERROR OWQ_parse_output_yesno(struct one_wire_query *owq)
195 {
196 if (OWQ_size(owq) < PROPERTY_LENGTH_YESNO) {
197 return -EMSGSIZE;
198 }
199 OWQ_buffer(owq)[0] = ((OWQ_Y(owq) & 0x1) == 0) ? '0' : '1';
200 return ShouldTrim(PN(owq))? 1 : PROPERTY_LENGTH_YESNO;
201 }
202
OWQ_format_output_offset_and_size_z(const char * string,struct one_wire_query * owq)203 ZERO_OR_ERROR OWQ_format_output_offset_and_size_z(const char *string, struct one_wire_query *owq)
204 {
205 SIZE_OR_ERROR ret = OWQ_parse_output_offset_and_size_z(string,owq) ;
206 if ( ret > 0 ) {
207 return 0 ;
208 }
209 return ret ;
210 }
211
OWQ_format_output_offset_and_size(const char * string,size_t length,struct one_wire_query * owq)212 ZERO_OR_ERROR OWQ_format_output_offset_and_size(const char *string, size_t length, struct one_wire_query *owq)
213 {
214 SIZE_OR_ERROR ret = OWQ_parse_output_offset_and_size(string,length,owq) ;
215 if ( ret > 0 ) {
216 return 0 ;
217 }
218 return ret ;
219 }
220
OWQ_parse_output_offset_and_size_z(const char * string,struct one_wire_query * owq)221 static SIZE_OR_ERROR OWQ_parse_output_offset_and_size_z(const char *string, struct one_wire_query *owq)
222 {
223 return OWQ_parse_output_offset_and_size(string, strlen(string), owq);
224 }
225
226 /* Put a string ionto the OWQ structure and return the length
227 check lengths and offsets as part of the process */
OWQ_parse_output_offset_and_size(const char * string,size_t length,struct one_wire_query * owq)228 static SIZE_OR_ERROR OWQ_parse_output_offset_and_size(const char *string, size_t length, struct one_wire_query *owq)
229 {
230 size_t copy_length = length;
231 off_t offset = OWQ_offset(owq);
232 Debug_Bytes("OWQ_parse_output_offset_and_size", (const BYTE *) string, length);
233
234 /* offset is after the length of the string -- return 0 since
235 some conditions a read after the end is done automatically */
236 if (offset > (off_t) length) {
237 return 0;
238 }
239
240 /* correct length for offset (cannot be negative because of previous check) */
241 copy_length -= offset;
242
243 /* correct length for buffer space */
244 if (copy_length > OWQ_size(owq)) {
245 copy_length = OWQ_size(owq);
246 }
247
248 /* and copy */
249 memcpy(OWQ_buffer(owq), &string[offset], copy_length);
250
251 // Warning, this will overwrite the I U or DATA value,
252 // but that shouldn't matter since it's only called on ascii values
253 // and all structure values
254 OWQ_length(owq) = copy_length;
255
256 return copy_length;
257 }
258
OWQ_parse_output_ascii(struct one_wire_query * owq)259 static SIZE_OR_ERROR OWQ_parse_output_ascii(struct one_wire_query *owq)
260 {
261 Debug_OWQ(owq);
262 return OWQ_length(owq);
263 }
264
OWQ_parse_output_array_with_commas(struct one_wire_query * owq)265 static SIZE_OR_ERROR OWQ_parse_output_array_with_commas(struct one_wire_query *owq)
266 {
267 struct one_wire_query owq_single;
268 size_t extension;
269 int len;
270 size_t used_size = 0;
271 size_t remaining_size = OWQ_size(owq);
272 size_t elements = OWQ_pn(owq).selected_filetype->ag->elements;
273
274 // loop though all array elements
275 for (extension = 0; extension < elements; ++extension) {
276 //printf("OWQ_parse_output_array_with_commas element=%d, size_used=%d, remaining=%d\n",(int)extension,(int)used_size,(int)remaining_size) ;
277 // Prepare a copy of owq that only points to a single element
278 memcpy(&owq_single, owq, sizeof(owq_single));
279 OWQ_pn(&owq_single).extension = extension;
280 memcpy(&OWQ_val(&owq_single), &OWQ_array(owq)[extension], sizeof(union value_object));
281 // add the comma first (if not the first element and enough room)
282 if (used_size > 0) {
283 if (remaining_size == 0) {
284 return -EFAULT;
285 }
286 OWQ_buffer(owq)[used_size] = ',';
287 ++used_size;
288 --remaining_size;
289 }
290 // Now process the single element
291 OWQ_buffer(&owq_single) = &OWQ_buffer(owq)[used_size];
292 OWQ_size(&owq_single) = remaining_size;
293 len = OWQ_parse_output(&owq_single);
294 // any error aborts
295 if (len < 0) {
296 return len;
297 }
298 remaining_size -= len;
299 used_size += len;
300 }
301 return used_size;
302 }
303
OWQ_parse_output_ascii_array(struct one_wire_query * owq)304 static SIZE_OR_ERROR OWQ_parse_output_ascii_array(struct one_wire_query *owq)
305 {
306 size_t extension;
307 size_t elements = OWQ_pn(owq).selected_filetype->ag->elements;
308 size_t total_length = 0;
309 size_t current_offset = OWQ_array_length(owq, 0);
310
311 for (extension = 0; extension < elements; ++extension) {
312 total_length += OWQ_array_length(owq, extension);
313 }
314
315 for (extension = 1; extension < elements; ++extension) {
316 memmove(&OWQ_buffer(owq)[current_offset + 1], &OWQ_buffer(owq)[current_offset], total_length - current_offset);
317 OWQ_buffer(owq)[current_offset] = ',';
318 ++total_length;
319 current_offset += 1 + OWQ_array_length(owq, extension);
320 }
321
322 return total_length;
323 }
324
OWQ_parse_output_array_no_commas(struct one_wire_query * owq)325 static SIZE_OR_ERROR OWQ_parse_output_array_no_commas(struct one_wire_query *owq)
326 {
327 size_t extension;
328 size_t total_length = 0;
329 size_t elements = OWQ_pn(owq).selected_filetype->ag->elements;
330
331 for (extension = 0; extension < elements; ++extension) {
332 total_length += OWQ_array_length(owq, extension);
333 }
334 return total_length;
335 }
336