]>
Commit | Line | Data |
---|---|---|
718e3744 | 1 | /* |
2 | * Buffering of output and input. | |
3 | * Copyright (C) 1998 Kunihiro Ishiguro | |
4 | * | |
5 | * This file is part of GNU Zebra. | |
6 | * | |
7 | * GNU Zebra is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published | |
9 | * by the Free Software Foundation; either version 2, or (at your | |
10 | * option) any later version. | |
11 | * | |
12 | * GNU Zebra is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with GNU Zebra; see the file COPYING. If not, write to the | |
19 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
20 | * Boston, MA 02111-1307, USA. | |
21 | */ | |
22 | ||
23 | #include <zebra.h> | |
24 | ||
25 | #include "memory.h" | |
26 | #include "buffer.h" | |
27 | ||
28 | /* Make buffer data. */ | |
29 | struct buffer_data * | |
30 | buffer_data_new (size_t size) | |
31 | { | |
32 | struct buffer_data *d; | |
33 | ||
34 | d = XMALLOC (MTYPE_BUFFER_DATA, sizeof (struct buffer_data)); | |
35 | memset (d, 0, sizeof (struct buffer_data)); | |
36 | d->data = XMALLOC (MTYPE_BUFFER_DATA, size); | |
37 | ||
38 | return d; | |
39 | } | |
40 | ||
41 | void | |
42 | buffer_data_free (struct buffer_data *d) | |
43 | { | |
44 | if (d->data) | |
45 | XFREE (MTYPE_BUFFER_DATA, d->data); | |
46 | XFREE (MTYPE_BUFFER_DATA, d); | |
47 | } | |
48 | ||
49 | /* Make new buffer. */ | |
50 | struct buffer * | |
51 | buffer_new (size_t size) | |
52 | { | |
53 | struct buffer *b; | |
54 | ||
55 | b = XMALLOC (MTYPE_BUFFER, sizeof (struct buffer)); | |
56 | memset (b, 0, sizeof (struct buffer)); | |
57 | ||
58 | b->size = size; | |
59 | ||
60 | return b; | |
61 | } | |
62 | ||
63 | /* Free buffer. */ | |
64 | void | |
65 | buffer_free (struct buffer *b) | |
66 | { | |
67 | struct buffer_data *d; | |
68 | struct buffer_data *next; | |
69 | ||
70 | d = b->head; | |
71 | while (d) | |
72 | { | |
73 | next = d->next; | |
74 | buffer_data_free (d); | |
75 | d = next; | |
76 | } | |
77 | ||
78 | d = b->unused_head; | |
79 | while (d) | |
80 | { | |
81 | next = d->next; | |
82 | buffer_data_free (d); | |
83 | d = next; | |
84 | } | |
85 | ||
86 | XFREE (MTYPE_BUFFER, b); | |
87 | } | |
88 | ||
89 | /* Make string clone. */ | |
90 | char * | |
91 | buffer_getstr (struct buffer *b) | |
92 | { | |
93 | return strdup ((char *)b->head->data); | |
94 | } | |
95 | ||
96 | /* Return 1 if buffer is empty. */ | |
97 | int | |
98 | buffer_empty (struct buffer *b) | |
99 | { | |
100 | if (b->tail == NULL || b->tail->cp == b->tail->sp) | |
101 | return 1; | |
102 | else | |
103 | return 0; | |
104 | } | |
105 | ||
106 | /* Clear and free all allocated data. */ | |
107 | void | |
108 | buffer_reset (struct buffer *b) | |
109 | { | |
110 | struct buffer_data *data; | |
111 | struct buffer_data *next; | |
112 | ||
113 | for (data = b->head; data; data = next) | |
114 | { | |
115 | next = data->next; | |
116 | buffer_data_free (data); | |
117 | } | |
118 | b->head = b->tail = NULL; | |
119 | b->alloc = 0; | |
120 | b->length = 0; | |
121 | } | |
122 | ||
123 | /* Add buffer_data to the end of buffer. */ | |
124 | void | |
125 | buffer_add (struct buffer *b) | |
126 | { | |
127 | struct buffer_data *d; | |
128 | ||
129 | d = buffer_data_new (b->size); | |
130 | ||
131 | if (b->tail == NULL) | |
132 | { | |
133 | d->prev = NULL; | |
134 | d->next = NULL; | |
135 | b->head = d; | |
136 | b->tail = d; | |
137 | } | |
138 | else | |
139 | { | |
140 | d->prev = b->tail; | |
141 | d->next = NULL; | |
142 | ||
143 | b->tail->next = d; | |
144 | b->tail = d; | |
145 | } | |
146 | ||
147 | b->alloc++; | |
148 | } | |
149 | ||
150 | /* Write data to buffer. */ | |
151 | int | |
152 | buffer_write (struct buffer *b, u_char *ptr, size_t size) | |
153 | { | |
154 | struct buffer_data *data; | |
155 | ||
156 | data = b->tail; | |
157 | b->length += size; | |
158 | ||
159 | /* We use even last one byte of data buffer. */ | |
160 | while (size) | |
161 | { | |
162 | /* If there is no data buffer add it. */ | |
163 | if (data == NULL || data->cp == b->size) | |
164 | { | |
165 | buffer_add (b); | |
166 | data = b->tail; | |
167 | } | |
168 | ||
169 | /* Last data. */ | |
170 | if (size <= (b->size - data->cp)) | |
171 | { | |
172 | memcpy ((data->data + data->cp), ptr, size); | |
173 | ||
174 | data->cp += size; | |
175 | size = 0; | |
176 | } | |
177 | else | |
178 | { | |
179 | memcpy ((data->data + data->cp), ptr, (b->size - data->cp)); | |
180 | ||
181 | size -= (b->size - data->cp); | |
182 | ptr += (b->size - data->cp); | |
183 | ||
184 | data->cp = b->size; | |
185 | } | |
186 | } | |
187 | return 1; | |
188 | } | |
189 | ||
190 | /* Insert character into the buffer. */ | |
191 | int | |
192 | buffer_putc (struct buffer *b, u_char c) | |
193 | { | |
194 | buffer_write (b, &c, 1); | |
195 | return 1; | |
196 | } | |
197 | ||
198 | /* Insert word (2 octets) into ther buffer. */ | |
199 | int | |
200 | buffer_putw (struct buffer *b, u_short c) | |
201 | { | |
202 | buffer_write (b, (char *)&c, 2); | |
203 | return 1; | |
204 | } | |
205 | ||
206 | /* Put string to the buffer. */ | |
207 | int | |
208 | buffer_putstr (struct buffer *b, u_char *c) | |
209 | { | |
210 | size_t size; | |
211 | ||
212 | size = strlen ((char *)c); | |
213 | buffer_write (b, c, size); | |
214 | return 1; | |
215 | } | |
216 | ||
217 | /* Flush specified size to the fd. */ | |
218 | void | |
219 | buffer_flush (struct buffer *b, int fd, size_t size) | |
220 | { | |
221 | int iov_index; | |
222 | struct iovec *iovec; | |
223 | struct buffer_data *data; | |
224 | struct buffer_data *out; | |
225 | struct buffer_data *next; | |
226 | ||
227 | iovec = malloc (sizeof (struct iovec) * b->alloc); | |
228 | iov_index = 0; | |
229 | ||
230 | for (data = b->head; data; data = data->next) | |
231 | { | |
232 | iovec[iov_index].iov_base = (char *)(data->data + data->sp); | |
233 | ||
234 | if (size <= (data->cp - data->sp)) | |
235 | { | |
236 | iovec[iov_index++].iov_len = size; | |
237 | data->sp += size; | |
238 | if (data->sp == data->cp) | |
239 | data = data->next; | |
240 | break; | |
241 | } | |
242 | else | |
243 | { | |
244 | iovec[iov_index++].iov_len = data->cp - data->sp; | |
245 | size -= data->cp - data->sp; | |
246 | data->sp = data->cp; | |
247 | } | |
248 | } | |
249 | ||
250 | /* Write buffer to the fd. */ | |
251 | writev (fd, iovec, iov_index); | |
252 | ||
253 | /* Free printed buffer data. */ | |
254 | for (out = b->head; out && out != data; out = next) | |
255 | { | |
256 | next = out->next; | |
257 | if (next) | |
258 | next->prev = NULL; | |
259 | else | |
260 | b->tail = next; | |
261 | b->head = next; | |
262 | ||
263 | buffer_data_free (out); | |
264 | b->alloc--; | |
265 | } | |
266 | ||
267 | free (iovec); | |
268 | } | |
269 | ||
270 | /* Flush all buffer to the fd. */ | |
271 | int | |
272 | buffer_flush_all (struct buffer *b, int fd) | |
273 | { | |
274 | int ret; | |
275 | struct buffer_data *d; | |
276 | int iov_index; | |
277 | struct iovec *iovec; | |
278 | ||
279 | if (buffer_empty (b)) | |
280 | return 0; | |
281 | ||
282 | iovec = malloc (sizeof (struct iovec) * b->alloc); | |
283 | iov_index = 0; | |
284 | ||
285 | for (d = b->head; d; d = d->next) | |
286 | { | |
287 | iovec[iov_index].iov_base = (char *)(d->data + d->sp); | |
288 | iovec[iov_index].iov_len = d->cp - d->sp; | |
289 | iov_index++; | |
290 | } | |
291 | ret = writev (fd, iovec, iov_index); | |
292 | ||
293 | free (iovec); | |
294 | ||
295 | buffer_reset (b); | |
296 | ||
297 | return ret; | |
298 | } | |
299 | ||
300 | /* Flush all buffer to the fd. */ | |
301 | int | |
302 | buffer_flush_vty_all (struct buffer *b, int fd, int erase_flag, | |
303 | int no_more_flag) | |
304 | { | |
305 | int nbytes; | |
306 | int iov_index; | |
307 | struct iovec *iov; | |
308 | struct iovec small_iov[3]; | |
309 | char more[] = " --More-- "; | |
310 | char erase[] = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, | |
311 | ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', | |
312 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; | |
313 | struct buffer_data *data; | |
314 | struct buffer_data *out; | |
315 | struct buffer_data *next; | |
316 | ||
317 | /* For erase and more data add two to b's buffer_data count.*/ | |
318 | if (b->alloc == 1) | |
319 | iov = small_iov; | |
320 | else | |
321 | iov = XCALLOC (MTYPE_TMP, sizeof (struct iovec) * (b->alloc + 2)); | |
322 | ||
323 | data = b->head; | |
324 | iov_index = 0; | |
325 | ||
326 | /* Previously print out is performed. */ | |
327 | if (erase_flag) | |
328 | { | |
329 | iov[iov_index].iov_base = erase; | |
330 | iov[iov_index].iov_len = sizeof erase; | |
331 | iov_index++; | |
332 | } | |
333 | ||
334 | /* Output data. */ | |
335 | for (data = b->head; data; data = data->next) | |
336 | { | |
337 | iov[iov_index].iov_base = (char *)(data->data + data->sp); | |
338 | iov[iov_index].iov_len = data->cp - data->sp; | |
339 | iov_index++; | |
340 | } | |
341 | ||
342 | /* In case of `more' display need. */ | |
343 | if (! buffer_empty (b) && !no_more_flag) | |
344 | { | |
345 | iov[iov_index].iov_base = more; | |
346 | iov[iov_index].iov_len = sizeof more; | |
347 | iov_index++; | |
348 | } | |
349 | ||
350 | /* We use write or writev*/ | |
351 | nbytes = writev (fd, iov, iov_index); | |
352 | ||
353 | /* Error treatment. */ | |
354 | if (nbytes < 0) | |
355 | { | |
356 | if (errno == EINTR) | |
357 | ; | |
358 | if (errno == EWOULDBLOCK) | |
359 | ; | |
360 | } | |
361 | ||
362 | /* Free printed buffer data. */ | |
363 | for (out = b->head; out && out != data; out = next) | |
364 | { | |
365 | next = out->next; | |
366 | if (next) | |
367 | next->prev = NULL; | |
368 | else | |
369 | b->tail = next; | |
370 | b->head = next; | |
371 | ||
372 | buffer_data_free (out); | |
373 | b->alloc--; | |
374 | } | |
375 | ||
376 | if (iov != small_iov) | |
377 | XFREE (MTYPE_TMP, iov); | |
378 | ||
379 | return nbytes; | |
380 | } | |
381 | ||
382 | /* Flush buffer to the file descriptor. Mainly used from vty | |
383 | interface. */ | |
384 | int | |
385 | buffer_flush_vty (struct buffer *b, int fd, int size, | |
386 | int erase_flag, int no_more_flag) | |
387 | { | |
388 | int nbytes; | |
389 | int iov_index; | |
390 | struct iovec *iov; | |
391 | struct iovec small_iov[3]; | |
392 | char more[] = " --More-- "; | |
393 | char erase[] = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, | |
394 | ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', | |
395 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; | |
396 | struct buffer_data *data; | |
397 | struct buffer_data *out; | |
398 | struct buffer_data *next; | |
399 | ||
400 | #ifdef IOV_MAX | |
401 | int iov_size; | |
402 | int total_size; | |
403 | struct iovec *c_iov; | |
404 | int c_nbytes; | |
405 | #endif /* IOV_MAX */ | |
406 | ||
407 | /* For erase and more data add two to b's buffer_data count.*/ | |
408 | if (b->alloc == 1) | |
409 | iov = small_iov; | |
410 | else | |
411 | iov = XCALLOC (MTYPE_TMP, sizeof (struct iovec) * (b->alloc + 2)); | |
412 | ||
413 | data = b->head; | |
414 | iov_index = 0; | |
415 | ||
416 | /* Previously print out is performed. */ | |
417 | if (erase_flag) | |
418 | { | |
419 | iov[iov_index].iov_base = erase; | |
420 | iov[iov_index].iov_len = sizeof erase; | |
421 | iov_index++; | |
422 | } | |
423 | ||
424 | /* Output data. */ | |
425 | for (data = b->head; data; data = data->next) | |
426 | { | |
427 | iov[iov_index].iov_base = (char *)(data->data + data->sp); | |
428 | ||
429 | if (size <= (data->cp - data->sp)) | |
430 | { | |
431 | iov[iov_index++].iov_len = size; | |
432 | data->sp += size; | |
433 | if (data->sp == data->cp) | |
434 | data = data->next; | |
435 | break; | |
436 | } | |
437 | else | |
438 | { | |
439 | iov[iov_index++].iov_len = data->cp - data->sp; | |
440 | size -= (data->cp - data->sp); | |
441 | data->sp = data->cp; | |
442 | } | |
443 | } | |
444 | ||
445 | /* In case of `more' display need. */ | |
446 | if (!buffer_empty (b) && !no_more_flag) | |
447 | { | |
448 | iov[iov_index].iov_base = more; | |
449 | iov[iov_index].iov_len = sizeof more; | |
450 | iov_index++; | |
451 | } | |
452 | ||
453 | /* We use write or writev*/ | |
454 | ||
455 | #ifdef IOV_MAX | |
456 | /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g. | |
457 | example: Solaris2.6 are defined IOV_MAX size at 16. */ | |
458 | c_iov = iov; | |
459 | total_size = iov_index; | |
460 | nbytes = 0; | |
461 | ||
462 | while( total_size > 0 ) | |
463 | { | |
464 | /* initialize write vector size at once */ | |
465 | iov_size = ( total_size > IOV_MAX ) ? IOV_MAX : total_size; | |
466 | ||
467 | c_nbytes = writev (fd, c_iov, iov_size ); | |
468 | ||
469 | if( c_nbytes < 0 ) | |
470 | { | |
471 | if(errno == EINTR) | |
472 | ; | |
473 | ; | |
474 | if(errno == EWOULDBLOCK) | |
475 | ; | |
476 | ; | |
477 | nbytes = c_nbytes; | |
478 | break; | |
479 | ||
480 | } | |
481 | ||
482 | nbytes += c_nbytes; | |
483 | ||
484 | /* move pointer io-vector */ | |
485 | c_iov += iov_size; | |
486 | total_size -= iov_size; | |
487 | } | |
488 | #else /* IOV_MAX */ | |
489 | nbytes = writev (fd, iov, iov_index); | |
490 | ||
491 | /* Error treatment. */ | |
492 | if (nbytes < 0) | |
493 | { | |
494 | if (errno == EINTR) | |
495 | ; | |
496 | if (errno == EWOULDBLOCK) | |
497 | ; | |
498 | } | |
499 | #endif /* IOV_MAX */ | |
500 | ||
501 | /* Free printed buffer data. */ | |
502 | for (out = b->head; out && out != data; out = next) | |
503 | { | |
504 | next = out->next; | |
505 | if (next) | |
506 | next->prev = NULL; | |
507 | else | |
508 | b->tail = next; | |
509 | b->head = next; | |
510 | ||
511 | buffer_data_free (out); | |
512 | b->alloc--; | |
513 | } | |
514 | ||
515 | if (iov != small_iov) | |
516 | XFREE (MTYPE_TMP, iov); | |
517 | ||
518 | return nbytes; | |
519 | } | |
520 | ||
521 | /* Calculate size of outputs then flush buffer to the file | |
522 | descriptor. */ | |
523 | int | |
524 | buffer_flush_window (struct buffer *b, int fd, int width, int height, | |
525 | int erase, int no_more) | |
526 | { | |
527 | unsigned long cp; | |
528 | unsigned long size; | |
529 | int lp; | |
530 | int lineno; | |
531 | struct buffer_data *data; | |
532 | ||
533 | if (height >= 2) | |
534 | height--; | |
535 | ||
536 | /* We have to calculate how many bytes should be written. */ | |
537 | lp = 0; | |
538 | lineno = 0; | |
539 | size = 0; | |
540 | ||
541 | for (data = b->head; data; data = data->next) | |
542 | { | |
543 | cp = data->sp; | |
544 | ||
545 | while (cp < data->cp) | |
546 | { | |
547 | if (data->data[cp] == '\n' || lp == width) | |
548 | { | |
549 | lineno++; | |
550 | if (lineno == height) | |
551 | { | |
552 | cp++; | |
553 | size++; | |
554 | goto flush; | |
555 | } | |
556 | lp = 0; | |
557 | } | |
558 | cp++; | |
559 | lp++; | |
560 | size++; | |
561 | } | |
562 | } | |
563 | ||
564 | /* Write data to the file descriptor. */ | |
565 | flush: | |
566 | ||
567 | return buffer_flush_vty (b, fd, size, erase, no_more); | |
568 | } |