Updated from canonical source location in GnuPG.
[gpgme.git] / assuan / assuan-buffer.c
1 /* assuan-buffer.c - read and send data
2  *      Copyright (C) 2001 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <assert.h>
27
28 #include "assuan-defs.h"
29
30
31 static int
32 writen ( int fd, const char *buffer, size_t length )
33 {
34   while (length)
35     {
36       int nwritten = write (fd, buffer, length);
37       
38       if (nwritten < 0)
39         {
40           if (errno == EINTR)
41             continue;
42           return -1; /* write error */
43         }
44       length -= nwritten;
45       buffer += nwritten;
46     }
47   return 0;  /* okay */
48 }
49
50 /* read an entire line */
51 static int
52 readline (int fd, char *buf, size_t buflen, int *r_nread, int *eof)
53 {
54   size_t nleft = buflen;
55   char *p;
56
57   *eof = 0;
58   *r_nread = 0;
59   while (nleft > 0)
60     {
61       int n = read (fd, buf, nleft);
62       if (n < 0)
63         {
64           if (errno == EINTR)
65             continue;
66           return -1; /* read error */
67         }
68       else if (!n)
69         {
70           *eof = 1;
71           break; /* allow incomplete lines */
72         }
73       p = buf;
74       nleft -= n;
75       buf += n;
76       *r_nread += n;
77       
78       for (; n && *p != '\n'; n--, p++)
79         ;
80       if (n)
81         break; /* at least one full line available - that's enough for now */
82     }
83
84   return 0;
85 }
86
87
88 int
89 _assuan_read_line (ASSUAN_CONTEXT ctx)
90 {
91   char *line = ctx->inbound.line;
92   int n, nread;
93   int rc;
94   
95   if (ctx->inbound.eof)
96     return -1;
97
98   if (ctx->inbound.attic.linelen)
99     {
100       memcpy (line, ctx->inbound.attic.line, ctx->inbound.attic.linelen);
101       nread = ctx->inbound.attic.linelen;
102       ctx->inbound.attic.linelen = 0;
103       for (n=0; n < nread && line[n] != '\n'; n++)
104         ;
105       if (n < nread)
106         rc = 0; /* found another line in the attic */
107       else
108         { /* read the rest */
109           n = nread;
110           assert (n < LINELENGTH);
111           rc = readline (ctx->inbound.fd, line + n, LINELENGTH - n,
112                          &nread, &ctx->inbound.eof);
113         }
114     }
115   else
116     rc = readline (ctx->inbound.fd, line, LINELENGTH,
117                    &nread, &ctx->inbound.eof);
118   if (rc)
119     return ASSUAN_Read_Error;
120   if (!nread)
121     {
122       assert (ctx->inbound.eof);
123       return -1; 
124     }
125
126   for (n=0; n < nread; n++)
127     {
128       if (line[n] == '\n')
129         {
130           if (n+1 < nread)
131             {
132               n++;
133               /* we have to copy the rest because the handlers are
134                  allowed to modify the passed buffer */
135               memcpy (ctx->inbound.attic.line, line+n, nread-n);
136               ctx->inbound.attic.linelen = nread-n;
137               n--;
138             }
139           if (n && line[n-1] == '\r')
140             n--;
141           line[n] = 0;
142           ctx->inbound.linelen = n;
143           return 0;
144         }
145     }
146
147   *line = 0;
148   ctx->inbound.linelen = 0;
149   return ctx->inbound.eof? ASSUAN_Line_Not_Terminated : ASSUAN_Line_Too_Long;
150 }
151
152
153
154
155 int 
156 _assuan_write_line (ASSUAN_CONTEXT ctx, const char *line )
157 {
158   int rc;
159
160   /* fixme: we should do some kind of line buffering */
161   rc = writen (ctx->outbound.fd, line, strlen(line));
162   if (rc)
163     rc = ASSUAN_Write_Error;
164   if (!rc)
165     {
166       rc = writen (ctx->outbound.fd, "\n", 1);
167       if (rc)
168         rc = ASSUAN_Write_Error;
169     }
170
171   return rc;
172 }
173
174
175 \f
176 /* Write out the data in buffer as datalines with line wrapping and
177    percent escaping.  This fucntion is used for GNU's custom streams */
178 int
179 _assuan_cookie_write_data (void *cookie, const char *buffer, size_t size)
180 {
181   ASSUAN_CONTEXT ctx = cookie;
182   char *line;
183   size_t linelen;
184
185   if (ctx->outbound.data.error)
186     return 0;
187
188   line = ctx->outbound.data.line;
189   linelen = ctx->outbound.data.linelen;
190   line += linelen;
191   while (size)
192     {
193       /* insert data line header */
194       if (!linelen)
195         {
196           *line++ = 'D';
197           *line++ = ' ';
198           linelen += 2;
199         }
200       
201       /* copy data, keep some space for the CRLF and to escape one character */
202       while (size && linelen < LINELENGTH-2-2)
203         {
204           if (*buffer == '%' || *buffer == '\r' || *buffer == '\n')
205             {
206               sprintf (line, "%%%02X", *(unsigned char*)buffer);
207               line += 3;
208               linelen += 3;
209               buffer++;
210             }
211           else
212             {
213               *line++ = *buffer++;
214               linelen++;
215             }
216           size--;
217         }
218       
219       if (linelen >= LINELENGTH-2-2)
220         {
221           *line++ = '\n';
222           linelen++;
223           if (writen (ctx->outbound.fd, ctx->outbound.data.line, linelen))
224             {
225               ctx->outbound.data.error = ASSUAN_Write_Error;
226               return 0;
227             }
228           line = ctx->outbound.data.line;
229           linelen = 0;
230         }
231     }
232
233   ctx->outbound.data.linelen = linelen;
234   return 0;
235 }
236
237
238 /* Write out any buffered data 
239    This fucntion is used for GNU's custom streams */
240 int
241 _assuan_cookie_write_flush (void *cookie)
242 {
243   ASSUAN_CONTEXT ctx = cookie;
244   char *line;
245   size_t linelen;
246
247   if (ctx->outbound.data.error)
248     return 0;
249
250   line = ctx->outbound.data.line;
251   linelen = ctx->outbound.data.linelen;
252   line += linelen;
253   if (linelen)
254     {
255       *line++ = '\n';
256       linelen++;
257       if (writen (ctx->outbound.fd, ctx->outbound.data.line, linelen))
258         {
259           ctx->outbound.data.error = ASSUAN_Write_Error;
260           return 0;
261         }
262       ctx->outbound.data.linelen = 0;
263     }
264   return 0;
265 }
266
267
268
269