2020#include <apr_hash.h>
2121#include "csv.h"
2222
23- #define MAX_CSV_LINE_SIZE 256
24-
25- int mod_okioki_buffer_append (char * buffer , size_t buffer_size , size_t * buffer_length , char * s , int s_length )
26- {
27- // If unknown, calculate the length of s.
28- if (s_length == -1 ) {
29- s_length = strlen (s );
30- }
31-
32- // Check if there is enough room in the buffer.
33- if (* buffer_length + s_length > buffer_size ) {
34- return HTTP_INTERNAL_SERVER_ERROR ;
35- }
36-
37- // Copy s into the buffer. We know the length, so no strcpy needed.
38- memcpy (& buffer [* buffer_length ], s , s_length );
39- * buffer_length += s_length ;
40- return HTTP_OK ;
41- }
42-
43- int mod_okioki_buffer_append_value (apr_pool_t * pool , char * buffer , size_t buffer_size , size_t * buffer_length , char * s , int s_length )
23+ int mod_okioki_buffer_append_value (apr_bucket_brigade * bb , apr_pool_t * pool , apr_bucket_alloc_t * alloc , char * s )
4424{
4525 int i , j ;
4626 int need_quote = 0 ;
47- int need_escape = 0 ;
48- char * s2 ;
4927 char c ;
50- int ret ;
51-
52- // If the length of s is unknown, calculate it.
53- if (s_length == -1 ) {
54- s_length = strlen (s );
55- }
28+ int s_length = strlen (s );
29+ apr_bucket * b ;
30+ apr_bucket * b_before = APR_BRIGADE_LAST (bb );
5631
57- // PASS 1: Check the string for any characters that need to be escaped .
58- for (i = 0 ; i < s_length ; i ++ ) {
32+ // Copy the string, and escape quotes .
33+ for (j = i = 0 ; i < s_length ; i ++ ) {
5934 c = s [i ];
6035 switch (c ) {
6136 case '"' :
62- need_escape ++ ; // fall tru
37+ // Add the text, before and including this quote.
38+ HTTP_ASSERT_NOT_NULL (
39+ b = apr_bucket_transient_create (& s [j ], (i - j ) + 1 , alloc ),
40+ HTTP_INTERNAL_SERVER_ERROR , "[mod_okioki] Could not allocate bucket."
41+ )
42+ APR_BRIGADE_INSERT_TAIL (bb , b );
43+
44+ // Double the quote.
45+ HTTP_ASSERT_NOT_NULL (
46+ b = apr_bucket_immortal_create ("\"" , 1 , alloc ),
47+ HTTP_INTERNAL_SERVER_ERROR , "[mod_okioki] Could not allocate bucket."
48+ )
49+ APR_BRIGADE_INSERT_TAIL (bb , b );
50+
51+ // Set j to just after this character.
52+ j = i + 1 ;
6353 case '\n' :
6454 case '\r' :
6555 case ',' :
6656 need_quote ++ ;
6757 break ;
6858 }
6959 }
70-
71- // PASS 2: If there was quote we need to escape for each quote.
72- if (need_escape ) {
73- // Escaping uses more space, so allocate a new string.
74- s2 = apr_palloc (pool , s_length + need_escape + 1 );
75-
76- // Copy the string, doubling a quote and ending with a nul.
77- for (j = i = 0 ; i < (s_length + 1 ); i ++ ) {
78- c = s [i ];
79- s2 [j ++ ] = c ;
80- if (c == '"' ) {
81- s2 [j ++ ] = c ;
82- }
83- }
84-
85- // After this we are going to use s like normally.
86- s = s2 ;
60+ if (j < i ) {
61+ // Add the last piece of text.
62+ HTTP_ASSERT_NOT_NULL (
63+ b = apr_bucket_transient_create (& s [j ], i - j , alloc ),
64+ HTTP_INTERNAL_SERVER_ERROR , "[mod_okioki] Could not allocate bucket."
65+ )
66+ APR_BRIGADE_INSERT_TAIL (bb , b );
8767 }
8868
89- // Add a quote if the string needed to be quoted.
9069 if (need_quote ) {
91- HTTP_ASSERT_OK (
92- ret = mod_okioki_buffer_append (buffer , buffer_size , buffer_length , "\"" , 1 ),
93- ret , "[mod_okioki] Not enough room to store CSV result."
70+ // Add a quote at the end of the value.
71+ HTTP_ASSERT_NOT_NULL (
72+ b = apr_bucket_immortal_create ("\"" , 1 , alloc ),
73+ HTTP_INTERNAL_SERVER_ERROR , "[mod_okioki] Could not allocate bucket."
9474 )
95- }
75+ APR_BRIGADE_INSERT_TAIL ( bb , b );
9676
97- // Add the "escaped" string.
98- HTTP_ASSERT_OK (
99- ret = mod_okioki_buffer_append (buffer , buffer_size , buffer_length , s , s_length + need_escape ),
100- ret , "[mod_okioki] Not enough room to store CSV result."
101- )
102-
103- // Add an other quote if the string needed to be quoted.
104- if (need_quote ) {
105- HTTP_ASSERT_OK (
106- ret = mod_okioki_buffer_append (buffer , buffer_size , buffer_length , "\"" , 1 ),
107- ret , "[mod_okioki] Not enough room to store CSV result."
77+ // Also add a quote before the value.
78+ HTTP_ASSERT_NOT_NULL (
79+ b = apr_bucket_immortal_create ("\"" , 1 , alloc ),
80+ HTTP_INTERNAL_SERVER_ERROR , "[mod_okioki] Could not allocate bucket."
10881 )
82+ APR_BUCKET_INSERT_AFTER (b_before , b );
10983 }
11084
11185 return HTTP_OK ;
11286}
11387
114- char * mod_okioki_generate_csv (apr_pool_t * pool , view_t * view , apr_array_header_t * result , size_t * result_s_len )
88+ apr_bucket_brigade * mod_okioki_generate_csv (apr_pool_t * pool , apr_bucket_alloc_t * alloc , view_t * view , apr_array_header_t * result )
11589{
11690 int i , j ;
11791 char * value ;
11892 apr_hash_t * row ;
93+ apr_bucket_brigade * bb ;
94+ apr_bucket * b ;
11995
120- size_t buffer_size = (result -> nelts + 1 ) * MAX_CSV_LINE_SIZE ;
121- size_t buffer_length = 0 ;
122- char * buffer = apr_palloc (pool , buffer_size );
123-
124- * result_s_len = 0 ;
125-
126- if (result == NULL || result -> nelts == 0 ) {
127- return "" ;
128- }
96+ HTTP_ASSERT_NOT_NULL (
97+ bb = apr_brigade_create (pool , alloc ),
98+ NULL , "[mod_okioki] Could not allocate a bucket brigade."
99+ )
129100
130101 // Create a csv header.
131102 for (i = 0 ; i < view -> nr_csv_params ; i ++ ) {
132103 // Add a comma between each entry.
133104 if (i != 0 ) {
134- HTTP_ASSERT_OK (
135- mod_okioki_buffer_append ( buffer , buffer_size , & buffer_length , "," , 1 ),
136- NULL , "[mod_okioki] Not enough room to store CSV result ."
105+ HTTP_ASSERT_NOT_NULL (
106+ b = apr_bucket_immortal_create ( "," , 1 , alloc ),
107+ NULL , "[mod_okioki] Could not allocate bucket ."
137108 )
109+ APR_BRIGADE_INSERT_TAIL (bb , b );
138110 }
139111
140112 // Copy the name of the column.
141- HTTP_ASSERT_OK (
142- mod_okioki_buffer_append_value ( pool , buffer , buffer_size , & buffer_length , view -> csv_params [i ], view -> csv_params_len [i ]),
143- NULL , "[mod_okioki] Not enough room to store CSV result ."
113+ HTTP_ASSERT_NOT_NULL (
114+ b = apr_bucket_immortal_create ( view -> csv_params [i ], view -> csv_params_len [i ], alloc ),
115+ NULL , "[mod_okioki] Could not allocate bucket ."
144116 )
117+ APR_BRIGADE_INSERT_TAIL (bb , b );
145118 }
146119 // Finish off the header with a carriage return and linefeed.
147- HTTP_ASSERT_OK (
148- mod_okioki_buffer_append ( buffer , buffer_size , & buffer_length , "\r\n" , 2 ),
149- NULL , "[mod_okioki] Not enough room to store CSV result ."
120+ HTTP_ASSERT_NOT_NULL (
121+ b = apr_bucket_immortal_create ( "\r\n" , 2 , alloc ),
122+ NULL , "[mod_okioki] Could not allocate bucket ."
150123 )
124+ APR_BRIGADE_INSERT_TAIL (bb , b );
151125
152126 // Check each row and figure out all the column names.
153127 for (j = 0 ; j < result -> nelts ; j ++ ) {
@@ -160,32 +134,39 @@ char *mod_okioki_generate_csv(apr_pool_t *pool, view_t *view, apr_array_header_t
160134 for (i = 0 ; i < view -> nr_csv_params ; i ++ ) {
161135 // Add a comma between each entry.
162136 if (i != 0 ) {
163- HTTP_ASSERT_OK (
164- mod_okioki_buffer_append ( buffer , buffer_size , & buffer_length , "," , 1 ),
165- NULL , "[mod_okioki] Not enough room to store CSV result ."
137+ HTTP_ASSERT_NOT_NULL (
138+ b = apr_bucket_immortal_create ( "," , 1 , alloc ),
139+ NULL , "[mod_okioki] Could not allocate bucket ."
166140 )
141+ APR_BRIGADE_INSERT_TAIL (bb , b );
167142 }
168143
169144 HTTP_ASSERT_NOT_NULL (
170145 value = apr_hash_get (row , view -> csv_params [i ], view -> csv_params_len [i ]),
171146 NULL , "[mod_okioki] column '%s' not found in result." , view -> csv_params [i ]
172147 )
173148
174- // Copy the name of the column.
149+ // Copy the value of the column.
175150 HTTP_ASSERT_OK (
176- mod_okioki_buffer_append_value (pool , buffer , buffer_size , & buffer_length , value , -1 ),
151+ mod_okioki_buffer_append_value (bb , pool , alloc , value ),
177152 NULL , "[mod_okioki] Not enough room to store CSV result."
178153 )
179154 }
180155
181156 // Finish off the header with a carriage return and linefeed.
182- HTTP_ASSERT_OK (
183- mod_okioki_buffer_append ( buffer , buffer_size , & buffer_length , "\r\n" , 2 ),
184- NULL , "[mod_okioki] Not enough room to store CSV result ."
157+ HTTP_ASSERT_NOT_NULL (
158+ b = apr_bucket_immortal_create ( "\r\n" , 2 , alloc ),
159+ NULL , "[mod_okioki] Could not allocate bucket ."
185160 )
161+ APR_BRIGADE_INSERT_TAIL (bb , b );
186162 }
187163
188- * result_s_len = buffer_length ;
189- return buffer ;
164+ // Add an end-of-stream.
165+ HTTP_ASSERT_NOT_NULL (
166+ b = apr_bucket_eos_create (alloc ),
167+ NULL , "[mod_okioki] Could not allocate bucket."
168+ )
169+ APR_BRIGADE_INSERT_TAIL (bb , b );
170+ return bb ;
190171}
191172
0 commit comments