No empty .Rs/.Re
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / global / dsn_util.c
blobc8b9baec049b258d1310bbd3bde8a0d9a15c48bb
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* dsn_util 3
6 /* SUMMARY
7 /* DSN status parsing routines
8 /* SYNOPSIS
9 /* #include <dsn_util.h>
11 /* #define DSN_SIZE ...
13 /* typedef struct { ... } DSN_BUF;
15 /* typedef struct {
16 /* .in +4
17 /* DSN_STAT dsn; /* RFC 3463 status */
18 /* const char *text; /* Free text */
19 /* .in -4
20 /* } DSN_SPLIT;
22 /* DSN_SPLIT *dsn_split(dp, def_dsn, text)
23 /* DSN_SPLIT *dp;
24 /* const char *def_dsn;
25 /* const char *text;
27 /* char *dsn_prepend(def_dsn, text)
28 /* const char *def_dsn;
29 /* const char *text;
31 /* size_t dsn_valid(text)
32 /* const char *text;
34 /* void DSN_UPDATE(dsn_buf, dsn, len)
35 /* DSN_BUF dsn_buf;
36 /* const char *dsn;
37 /* size_t len;
39 /* const char *DSN_CODE(dsn_buf)
40 /* DSN_BUF dsn_buf;
42 /* char *DSN_CLASS(dsn_buf)
43 /* DSN_BUF dsn_buf;
44 /* DESCRIPTION
45 /* The functions in this module manipulate pairs of RFC 3463
46 /* status codes and descriptive free text.
48 /* dsn_split() splits text into an RFC 3463 status code and
49 /* descriptive free text. When the text does not start with
50 /* a status code, the specified default status code is used
51 /* instead. Whitespace before the optional status code or
52 /* text is skipped. dsn_split() returns a copy of the RFC
53 /* 3463 status code, and returns a pointer to (not copy of)
54 /* the remainder of the text. The result value is the first
55 /* argument.
57 /* dsn_prepend() prepends the specified default RFC 3463 status
58 /* code to the specified text if no status code is present in
59 /* the text. This function produces the same result as calling
60 /* concatenate() with the results from dsn_split(). The result
61 /* should be passed to myfree(). Whitespace before the optional
62 /* status code or text is skipped.
64 /* dsn_valid() returns the length of the RFC 3463 status code
65 /* at the beginning of text, or zero. It does not skip initial
66 /* whitespace.
68 /* Arguments:
69 /* .IP def_dsn
70 /* Null-terminated default RFC 3463 status code that will be
71 /* used when the free text does not start with one.
72 /* .IP dp
73 /* Pointer to storage for copy of DSN status code, and for
74 /* pointer to free text.
75 /* .IP dsn
76 /* Null-terminated RFC 3463 status code.
77 /* .IP text
78 /* Null-terminated free text.
79 /* SEE ALSO
80 /* msg(3) diagnostics interface
81 /* DIAGNOSTICS
82 /* Panic: invalid default DSN code.
83 /* LICENSE
84 /* .ad
85 /* .fi
86 /* The Secure Mailer license must be distributed with this software.
87 /* AUTHOR(S)
88 /* Wietse Venema
89 /* IBM T.J. Watson Research
90 /* P.O. Box 704
91 /* Yorktown Heights, NY 10598, USA
92 /*--*/
94 /* System library. */
96 #include <sys_defs.h>
97 #include <stdarg.h>
98 #include <string.h>
99 #include <ctype.h>
101 /* Utility library. */
103 #include <msg.h>
104 #include <mymalloc.h>
105 #include <vstring.h>
106 #include <stringops.h>
108 /* Global library. */
110 #include <dsn_util.h>
112 /* dsn_valid - check RFC 3463 enhanced status code, return length or zero */
114 size_t dsn_valid(const char *text)
116 const unsigned char *cp = (unsigned char *) text;
117 size_t len;
119 /* First portion is one digit followed by dot. */
120 if ((cp[0] != '2' && cp[0] != '4' && cp[0] != '5') || cp[1] != '.')
121 return (0);
123 /* Second portion is 1-3 digits followed by dot. */
124 cp += 2;
125 if ((len = strspn((char *) cp, "0123456789")) < 1 || len > DSN_DIGS2
126 || cp[len] != '.')
127 return (0);
129 /* Last portion is 1-3 digits followed by end-of-string or whitespace. */
130 cp += len + 1;
131 if ((len = strspn((char *) cp, "0123456789")) < 1 || len > DSN_DIGS3
132 || (cp[len] != 0 && !ISSPACE(cp[len])))
133 return (0);
135 return (((char *) cp - text) + len);
138 /* dsn_split - split text into DSN and text */
140 DSN_SPLIT *dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text)
142 const char *myname = "dsn_split";
143 const char *cp = text;
144 size_t len;
147 * Look for an optional RFC 3463 enhanced status code.
149 * XXX If we want to enforce that the first digit of the status code in the
150 * text matches the default status code, then pipe_command() needs to be
151 * changed. It currently auto-detects the reply code without knowing in
152 * advance if the result will start with '4' or '5'.
154 while (ISSPACE(*cp))
155 cp++;
156 if ((len = dsn_valid(cp)) > 0) {
157 strncpy(dp->dsn.data, cp, len);
158 dp->dsn.data[len] = 0;
159 cp += len + 1;
160 } else if ((len = dsn_valid(def_dsn)) > 0) {
161 strncpy(dp->dsn.data, def_dsn, len);
162 dp->dsn.data[len] = 0;
163 } else {
164 msg_panic("%s: bad default status \"%s\"", myname, def_dsn);
168 * The remainder is free text.
170 while (ISSPACE(*cp))
171 cp++;
172 dp->text = cp;
174 return (dp);
177 /* dsn_prepend - prepend optional status to text, result on heap */
179 char *dsn_prepend(const char *def_dsn, const char *text)
181 DSN_SPLIT dp;
183 dsn_split(&dp, def_dsn, text);
184 return (concatenate(DSN_STATUS(dp.dsn), " ", dp.text, (char *) 0));