Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / global / record.c
blob8cc844add0bd1cfc6acfe6e73dfa2af6ae74aabf
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* record 3
6 /* SUMMARY
7 /* simple typed record I/O
8 /* SYNOPSIS
9 /* #include <record.h>
11 /* int rec_get(stream, buf, maxsize)
12 /* VSTREAM *stream;
13 /* VSTRING *buf;
14 /* ssize_t maxsize;
16 /* int rec_get_raw(stream, buf, maxsize, flags)
17 /* VSTREAM *stream;
18 /* VSTRING *buf;
19 /* ssize_t maxsize;
20 /* int flags;
22 /* int rec_put(stream, type, data, len)
23 /* VSTREAM *stream;
24 /* int type;
25 /* const char *data;
26 /* ssize_t len;
27 /* AUXILIARY FUNCTIONS
28 /* int rec_put_type(stream, type, offset)
29 /* VSTREAM *stream;
30 /* int type;
31 /* long offset;
33 /* int rec_fprintf(stream, type, format, ...)
34 /* VSTREAM *stream;
35 /* int type;
36 /* const char *format;
38 /* int rec_fputs(stream, type, str)
39 /* VSTREAM *stream;
40 /* int type;
41 /* const char *str;
43 /* int REC_PUT_BUF(stream, type, buf)
44 /* VSTREAM *stream;
45 /* int type;
46 /* VSTRING *buf;
48 /* int rec_vfprintf(stream, type, format, ap)
49 /* VSTREAM *stream;
50 /* int type;
51 /* const char *format;
52 /* va_list ap;
54 /* int rec_goto(stream, where)
55 /* VSTREAM *stream;
56 /* const char *where;
58 /* int rec_pad(stream, type, len)
59 /* VSTREAM *stream;
60 /* int type;
61 /* int len;
63 /* REC_SPACE_NEED(buflen, reclen)
64 /* ssize_t buflen;
65 /* ssize_t reclen;
66 /* DESCRIPTION
67 /* This module reads and writes typed variable-length records.
68 /* Each record contains a 1-byte type code (0..255), a length
69 /* (1 or more bytes) and as much data as the length specifies.
71 /* rec_get_raw() retrieves a record from the named record stream
72 /* and returns the record type. The \fImaxsize\fR argument is
73 /* zero, or specifies a maximal acceptable record length.
74 /* The result is REC_TYPE_EOF when the end of the file was reached,
75 /* and REC_TYPE_ERROR in case of a bad record. The result buffer is
76 /* null-terminated for convenience. Records may contain embedded
77 /* null characters. The \fIflags\fR argument specifies zero or
78 /* more of the following:
79 /* .IP REC_FLAG_FOLLOW_PTR
80 /* Follow PTR records, instead of exposing them to the application.
81 /* .IP REC_FLAG_SKIP_DTXT
82 /* Skip "deleted text" records, instead of exposing them to
83 /* the application.
84 /* .IP REC_FLAG_SEEK_END
85 /* Seek to the end-of-file upon reading a REC_TYPE_END record.
86 /* .PP
87 /* Specify REC_FLAG_NONE to request no special processing,
88 /* and REC_FLAG_DEFAULT for normal use.
90 /* rec_get() is a wrapper around rec_get_raw() that always
91 /* enables the REC_FLAG_FOLLOW_PTR, REC_FLAG_SKIP_DTXT
92 /* and REC_FLAG_SEEK_END features.
94 /* rec_put() stores the specified record and returns the record
95 /* type, or REC_TYPE_ERROR in case of problems.
97 /* rec_put_type() updates the type field of the record at the
98 /* specified file offset. The result is the new record type,
99 /* or REC_TYPE_ERROR in case of trouble.
101 /* rec_fprintf() and rec_vfprintf() format their arguments and
102 /* write the result to the named stream. The result is the same
103 /* as with rec_put().
105 /* rec_fputs() writes a record with as contents a copy of the
106 /* specified string. The result is the same as with rec_put().
108 /* REC_PUT_BUF() is a wrapper for rec_put() that makes it
109 /* easier to handle VSTRING buffers. It is an unsafe macro
110 /* that evaluates some arguments more than once.
112 /* rec_goto() takes the argument of a pointer record and moves
113 /* the file pointer to the specified location. A zero position
114 /* means do nothing. The result is REC_TYPE_ERROR in case of
115 /* failure.
117 /* rec_pad() writes a record that occupies the larger of (the
118 /* specified amount) or (an implementation-defined minimum).
120 /* REC_SPACE_NEED(buflen, reclen) converts the specified buffer
121 /* length into a record length. This macro modifies its second
122 /* argument.
123 /* DIAGNOSTICS
124 /* Panics: interface violations. Fatal errors: insufficient memory.
125 /* Warnings: corrupted file.
126 /* LICENSE
127 /* .ad
128 /* .fi
129 /* The Secure Mailer license must be distributed with this software.
130 /* AUTHOR(S)
131 /* Wietse Venema
132 /* IBM T.J. Watson Research
133 /* P.O. Box 704
134 /* Yorktown Heights, NY 10598, USA
135 /*--*/
137 /* System library. */
139 #include <sys_defs.h>
140 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */
141 #include <stdarg.h>
142 #include <unistd.h>
143 #include <string.h>
145 #ifndef NBBY
146 #define NBBY 8 /* XXX should be in sys_defs.h */
147 #endif
149 /* Utility library. */
151 #include <msg.h>
152 #include <mymalloc.h>
153 #include <vstream.h>
154 #include <vstring.h>
155 #include <stringops.h>
157 /* Global library. */
159 #include <off_cvt.h>
160 #include <rec_type.h>
161 #include <record.h>
163 /* rec_put_type - update record type field */
165 int rec_put_type(VSTREAM *stream, int type, off_t offset)
167 if (type < 0 || type > 255)
168 msg_panic("rec_put_type: bad record type %d", type);
170 if (msg_verbose > 2)
171 msg_info("rec_put_type: %d at %ld", type, (long) offset);
173 if (vstream_fseek(stream, offset, SEEK_SET) < 0
174 || VSTREAM_PUTC(type, stream) != type) {
175 return (REC_TYPE_ERROR);
176 } else {
177 return (type);
181 /* rec_put - store typed record */
183 int rec_put(VSTREAM *stream, int type, const char *data, ssize_t len)
185 ssize_t len_rest;
186 int len_byte;
188 if (type < 0 || type > 255)
189 msg_panic("rec_put: bad record type %d", type);
191 if (msg_verbose > 2)
192 msg_info("rec_put: type %c len %ld data %.10s",
193 type, (long) len, data);
196 * Write the record type, one byte.
198 if (VSTREAM_PUTC(type, stream) == VSTREAM_EOF)
199 return (REC_TYPE_ERROR);
202 * Write the record data length in 7-bit portions, using the 8th bit to
203 * indicate that there is more. Use as many length bytes as needed.
205 len_rest = len;
206 do {
207 len_byte = len_rest & 0177;
208 if (len_rest >>= 7U)
209 len_byte |= 0200;
210 if (VSTREAM_PUTC(len_byte, stream) == VSTREAM_EOF) {
211 return (REC_TYPE_ERROR);
213 } while (len_rest != 0);
216 * Write the record data portion. Use as many length bytes as needed.
218 if (len && vstream_fwrite(stream, data, len) != len)
219 return (REC_TYPE_ERROR);
220 return (type);
223 /* rec_get_raw - retrieve typed record */
225 int rec_get_raw(VSTREAM *stream, VSTRING *buf, ssize_t maxsize, int flags)
227 const char *myname = "rec_get";
228 int type;
229 ssize_t len;
230 int len_byte;
231 unsigned shift;
234 * Sanity check.
236 if (maxsize < 0)
237 msg_panic("%s: bad record size limit: %ld", myname, (long) maxsize);
239 for (;;) {
242 * Extract the record type.
244 if ((type = VSTREAM_GETC(stream)) == VSTREAM_EOF)
245 return (REC_TYPE_EOF);
248 * Find out the record data length. Return an error result when the
249 * record data length is malformed or when it exceeds the acceptable
250 * limit.
252 for (len = 0, shift = 0; /* void */ ; shift += 7) {
253 if (shift >= (int) (NBBY * sizeof(int))) {
254 msg_warn("%s: too many length bits, record type %d",
255 VSTREAM_PATH(stream), type);
256 return (REC_TYPE_ERROR);
258 if ((len_byte = VSTREAM_GETC(stream)) == VSTREAM_EOF) {
259 msg_warn("%s: unexpected EOF reading length, record type %d",
260 VSTREAM_PATH(stream), type);
261 return (REC_TYPE_ERROR);
263 len |= (len_byte & 0177) << shift;
264 if ((len_byte & 0200) == 0)
265 break;
267 if (len < 0 || (maxsize > 0 && len > maxsize)) {
268 msg_warn("%s: illegal length %ld, record type %d",
269 VSTREAM_PATH(stream), (long) len, type);
270 while (len-- > 0 && VSTREAM_GETC(stream) != VSTREAM_EOF)
271 /* void */ ;
272 return (REC_TYPE_ERROR);
276 * Reserve buffer space for the result, and read the record data into
277 * the buffer.
279 VSTRING_RESET(buf);
280 VSTRING_SPACE(buf, len);
281 if (vstream_fread(stream, vstring_str(buf), len) != len) {
282 msg_warn("%s: unexpected EOF in data, record type %d length %ld",
283 VSTREAM_PATH(stream), type, (long) len);
284 return (REC_TYPE_ERROR);
286 VSTRING_AT_OFFSET(buf, len);
287 VSTRING_TERMINATE(buf);
288 if (msg_verbose > 2)
289 msg_info("%s: type %c len %ld data %.10s", myname,
290 type, (long) len, vstring_str(buf));
293 * Transparency options.
295 if (flags == 0)
296 break;
297 if (type == REC_TYPE_PTR && (flags & REC_FLAG_FOLLOW_PTR) != 0
298 && (type = rec_goto(stream, vstring_str(buf))) != REC_TYPE_ERROR)
299 continue;
300 if (type == REC_TYPE_DTXT && (flags & REC_FLAG_SKIP_DTXT) != 0)
301 continue;
302 if (type == REC_TYPE_END && (flags & REC_FLAG_SEEK_END) != 0)
303 (void) vstream_fseek(stream, (off_t) 0, SEEK_END);
304 break;
306 return (type);
309 /* rec_goto - follow PTR record */
311 int rec_goto(VSTREAM *stream, const char *buf)
313 off_t offset;
314 static const char *saved_path;
315 static off_t saved_offset;
316 static int reverse_count;
319 * Crude workaround for queue file loops. VSTREAMs currently have no
320 * option to attach application-specific data, so we use global state and
321 * simple logic to detect if an application switches streams. We trigger
322 * on reverse jumps only. There's one reverse jump for every inserted
323 * header, but only one reverse jump for all appended recipients. No-one
324 * is likely to insert 10000 message headers, but someone might append
325 * 10000 recipients.
327 #define STREQ(x,y) ((x) == (y) && strcmp((x), (y)) == 0)
328 #define REVERSE_JUMP_LIMIT 10000
330 if (!STREQ(saved_path, VSTREAM_PATH(stream))) {
331 saved_path = VSTREAM_PATH(stream);
332 reverse_count = 0;
333 saved_offset = 0;
335 while (ISSPACE(*buf))
336 buf++;
337 if ((offset = off_cvt_string(buf)) < 0) {
338 msg_warn("%s: malformed pointer record value: %s",
339 VSTREAM_PATH(stream), buf);
340 return (REC_TYPE_ERROR);
341 } else if (offset == 0) {
342 /* Dummy record. */
343 return (0);
344 } else if (offset <= saved_offset && ++reverse_count > REVERSE_JUMP_LIMIT) {
345 msg_warn("%s: too many reverse jump records", VSTREAM_PATH(stream));
346 return (REC_TYPE_ERROR);
347 } else if (vstream_fseek(stream, offset, SEEK_SET) < 0) {
348 msg_warn("%s: seek error after pointer record: %m",
349 VSTREAM_PATH(stream));
350 return (REC_TYPE_ERROR);
351 } else {
352 saved_offset = offset;
353 return (0);
357 /* rec_vfprintf - write formatted string to record */
359 int rec_vfprintf(VSTREAM *stream, int type, const char *format, va_list ap)
361 static VSTRING *vp;
363 if (vp == 0)
364 vp = vstring_alloc(100);
367 * Writing a formatted string involves an extra copy, because we must
368 * know the record length before we can write it.
370 vstring_vsprintf(vp, format, ap);
371 return (REC_PUT_BUF(stream, type, vp));
374 /* rec_fprintf - write formatted string to record */
376 int rec_fprintf(VSTREAM *stream, int type, const char *format,...)
378 int result;
379 va_list ap;
381 va_start(ap, format);
382 result = rec_vfprintf(stream, type, format, ap);
383 va_end(ap);
384 return (result);
387 /* rec_fputs - write string to record */
389 int rec_fputs(VSTREAM *stream, int type, const char *str)
391 return (rec_put(stream, type, str, str ? strlen(str) : 0));
394 /* rec_pad - write padding record */
396 int rec_pad(VSTREAM *stream, int type, int len)
398 int width = len - 2; /* type + length */
400 return (rec_fprintf(stream, type, "%*s",
401 width < 1 ? 1 : width, "0"));