1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: detoken.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006 University of Washington
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * ========================================================================
18 #include "../pith/headers.h"
19 #include "../pith/detoken.h"
20 #include "../pith/state.h"
21 #include "../pith/conf.h"
22 #include "../pith/status.h"
23 #include "../pith/pattern.h"
24 #include "../pith/reply.h"
25 #include "../pith/mailindx.h"
26 #include "../pith/options.h"
30 * Hook to read signature from local file
32 char *(*pith_opt_get_signature_file
)(char *, int, int, int);
38 char *detoken_guts(char *, int, ENVELOPE
*, ACTION_S
*, REDRAFT_POS_S
**, int, int *);
39 char *handle_if_token(char *, char *, int, ENVELOPE
*, ACTION_S
*, char **);
40 char *get_token_arg(char *, char **);
44 * Detokenize signature or template files.
46 * If is_sig, we always use literal sigs before sigfiles if they are
47 * defined. So, check for role->litsig and use it. If it doesn't exist, use
48 * the global literal sig if defined. Else the role->sig file or the
49 * global signature file.
51 * If !is_sig, use role->template.
53 * So we start with a literal signature or a signature or template file.
54 * If that's a file, we read it first. The file could be remote.
55 * Then we detokenize the literal signature or file contents and return
56 * an allocated string which the caller frees.
58 * Args role -- See above about what happens depending on is_sig.
59 * relative to the pinerc dir.
60 * env -- The envelope to use for detokenizing. May be NULL.
61 * prenewlines -- How many blank lines should be included at start.
62 * postnewlines -- How many blank lines should be included after.
63 * is_sig -- This is a signature (not a template file).
64 * redraft_pos -- This is a return value. If it is non-NULL coming in,
65 * then the cursor position is returned here.
66 * impl -- This is a combination argument which is both an input
67 * argument and a return value. If it is non-NULL and = 0,
68 * that means that we want the cursor position returned here,
69 * even if that position is set implicitly to the end of
70 * the output string. If it is = 1 coming in, that means
71 * we only want the cursor position to be set if it is set
72 * explicitly. If it is 2, or if redraft_pos is NULL,
73 * we don't set it at all.
74 * If the cursor position gets set explicitly by a
75 * _CURSORPOS_ token in the file then this is set to 2
76 * on return. If the cursor position is set implicitly to
77 * the end of the included file, then this is set to 1
80 * Returns -- An allocated string is returned.
83 detoken(ACTION_S
*role
, ENVELOPE
*env
, int prenewlines
, int postnewlines
,
84 int is_sig
, REDRAFT_POS_S
**redraft_pos
, int *impl
)
93 * If role->litsig is set, we use it;
94 * Else, if VAR_LITERAL_SIG is set, we use that;
95 * Else, if role->sig is set, we use that;
96 * Else, if VAR_SIGNATURE_FILE is set, we use that.
97 * This can be a little surprising if you set the VAR_LITERAL_SIG
98 * and don't set a role->litsig but do set a role->sig. The
99 * VAR_LITERAL_SIG will be used, not role->sig. The reason for this
100 * is mostly that it is much easier to display the right stuff
101 * in the various config screens if we do it that way. Besides,
102 * people will typically use only literal sigs or only sig files,
103 * there is no reason to mix them, so we don't provide support to
106 if(role
&& role
->litsig
)
107 literal_sig
= role
->litsig
;
108 else if(ps_global
->VAR_LITERAL_SIG
)
109 literal_sig
= ps_global
->VAR_LITERAL_SIG
;
110 else if(role
&& role
->sig
)
113 sigfile
= ps_global
->VAR_SIGNATURE_FILE
;
115 else if(role
&& role
->template)
116 sigfile
= role
->template;
119 src
= get_signature_lit(literal_sig
, prenewlines
, postnewlines
, is_sig
,1);
120 else if(sigfile
&& pith_opt_get_signature_file
)
121 src
= (*pith_opt_get_signature_file
)(sigfile
, prenewlines
, postnewlines
, is_sig
);
125 ret
= detoken_src(src
, FOR_TEMPLATE
, env
, role
, redraft_pos
, impl
);
127 fs_give((void **)&src
);
135 * Filter the source string from the template file and return an allocated
136 * copy of the result with text replacements for the tokens.
137 * Fill in offset in redraft_pos.
139 * This is really inefficient but who cares? It's just cpu time.
142 detoken_src(char *src
, int for_what
, ENVELOPE
*env
, ACTION_S
*role
,
143 REDRAFT_POS_S
**redraft_pos
, int *impl
)
145 int loopcnt
= 25; /* just in case, avoid infinite loop */
146 char *ret
, *str1
, *str2
;
153 * We keep running it through until it stops changing so user can
154 * nest calls to token stuff.
158 /* short-circuit if no chance it will change */
159 if(strindex(str1
, '_'))
160 str2
= detoken_guts(str1
, for_what
, env
, role
, NULL
, 0, NULL
);
164 if(str1
&& str2
&& (str1
== str2
|| !strcmp(str1
, str2
))){
165 done
++; /* It stopped changing */
166 if(str1
&& str1
!= src
&& str1
!= str2
)
167 fs_give((void **)&str1
);
169 else{ /* Still changing */
170 if(str1
&& str1
!= src
&& str1
!= str2
)
171 fs_give((void **)&str1
);
176 } while(str2
&& !done
&& loopcnt
-- > 0);
179 * Have to run it through once more to get the redraft_pos and
180 * to remove any backslash escape for a token.
182 if((str2
&& strindex(str2
, '_')) ||
183 (impl
&& *impl
== 0 && redraft_pos
&& !*redraft_pos
)){
184 ret
= detoken_guts(str2
, for_what
, env
, role
, redraft_pos
, 1, impl
);
186 fs_give((void **)&str2
);
200 * The guts of the detokenizing routines. Filter the src string looking for
201 * tokens and replace them with the appropriate text. In the case of the
202 * cursor_pos token we set redraft_pos instead.
204 * Args src -- The source string
206 * env -- Envelope to look in for token replacements.
207 * redraft_pos -- Return the redraft offset here, if non-zero.
208 * last_pass -- This is a flag to tell detoken_guts whether or not to do
209 * the replacement for _CURSORPOS_. Leave it as is until
210 * the last pass. We need this because we want to defer
211 * cursor placement until the very last call to detoken,
212 * otherwise we'd have to keep track of the cursor
213 * position as subsequent text replacements (nested)
215 * This same flag is also used to decide when to eliminate
216 * backslash escapes from in front of tokens. The only
217 * use of backslash escapes is to escape an entire token.
218 * That is, \_DATE_ is a literal _DATE_, but any other
219 * backslash is a literal backslash. That way, nobody
220 * but wackos will have to worry about backslashes.
221 * impl -- This is a combination argument which is both an input
222 * argument and a return value. If it is non-NULL and 0
223 * coming in, that means that we should set redraft_pos,
224 * even if that position is set implicitly to the end of
225 * the output string. If it is 1 coming in, that means
226 * we only want the cursor position to be set if it is set
227 * explicitly. If it is 2 coming in (or if
228 * redraft_pos is NULL) then we don't set it at all.
229 * If the cursor position gets set explicitly by a
230 * _CURSORPOS_ token in the file then this is set to 2
231 * on return. If the cursor position is set implicitly to
232 * the end of the included file, then this is set to 1
235 * Returns pointer to alloced result
238 detoken_guts(char *src
, int for_what
, ENVELOPE
*env
, ACTION_S
*role
,
239 REDRAFT_POS_S
**redraft_pos
, int last_pass
, int *impl
)
242 char *p
, *q
= NULL
, *dst
= NULL
;
243 char subbuf
[MAXSUB
+1], *repl
;
246 int sizing_pass
= 1, suppress_tokens
= 0;
254 * The tokens we look for begin with _. The only escaping mechanism
255 * is a backslash in front of a token. This will give you the literal
256 * token. So \_DATE_ is a literal _DATE_.
257 * Tokens like _word_ are replaced with the appropriate text if
258 * word is recognized. If _word_ is followed immediately by a left paren
259 * it is an if-else thingie. _word_(match_this,if_text,else_text) means to
260 * replace that with either the if_text or else_text depending on whether
261 * what _word_ (without the paren) would produce matches match_this or not.
266 case '_': /* possible start of token */
267 if(!suppress_tokens
&&
268 (pt
= itoktype(p
+1, for_what
| DELIM_USCORE
)) != NULL
){
269 char *free_this
= NULL
;
271 p
+= (strlen(pt
->name
) + 2); /* skip over token */
276 if(pt
->ctype
== iCursorPos
){
277 if(!last_pass
){ /* put it back */
279 strncpy(subbuf
+1, pt
->name
, sizeof(subbuf
)-2);
280 subbuf
[sizeof(subbuf
)-1] = '\0';
281 strncat(subbuf
, "_", sizeof(subbuf
)-strlen(subbuf
)-1);
282 subbuf
[sizeof(subbuf
)-1] = '\0';
290 if(redraft_pos
&& impl
&& *impl
!= 2){
293 (REDRAFT_POS_S
*)fs_get(sizeof(**redraft_pos
));
294 memset((void *)*redraft_pos
, 0,
295 sizeof(**redraft_pos
));
296 (*redraft_pos
)->hdrname
= cpystr(":");
299 (*redraft_pos
)->offset
= l
;
300 *impl
= 2; /* set explicitly */
304 else if(pt
->what_for
& FOR_REPLY_INTRO
)
305 repl
= get_reply_data(env
, role
, pt
->ctype
,
306 subbuf
, sizeof(subbuf
)-1);
308 if(*p
== LPAREN
){ /* if-else construct */
311 repl
= free_this
= handle_if_token(repl
, p
, for_what
,
319 cnt
+= (long)strlen(repl
);
321 strncpy(q
, repl
, cnt
-(q
-dst
));
328 fs_give((void **)&free_this
);
330 else{ /* unrecognized token, treat it just like text */
334 else if(q
-dst
< cnt
+1)
344 * If a real token follows the backslash, then the backslash
345 * is here to escape the token. Otherwise, it's just a
349 ((pt
= itoktype(p
+2, for_what
| DELIM_USCORE
)) != NULL
)){
351 * Backslash is escape for literal token.
352 * If we're on the last pass we want to eliminate the
353 * backslash, otherwise we keep it.
354 * In either case, suppress_tokens will cause the token
355 * lookup to be skipped above so that the token will
356 * be treated as literal text.
363 /* else, fall through and keep backslash */
365 /* this is a literal backslash, fall through */
370 else if(q
-dst
< cnt
+1)
371 *q
++ = *p
; /* copy the character */
378 if(!sizing_pass
&& q
-dst
< cnt
+1)
384 * Now we're done figuring out how big the answer will be. We
385 * allocate space for it and go back through filling it in.
388 q
= dst
= (char *)fs_get((cnt
+ 1) * sizeof(char));
393 * Set redraft_pos to character following the template, unless
394 * it has already been set.
396 if(dst
&& impl
&& *impl
== 0 && redraft_pos
&& !*redraft_pos
){
397 *redraft_pos
= (REDRAFT_POS_S
*)fs_get(sizeof(**redraft_pos
));
398 memset((void *)*redraft_pos
, 0, sizeof(**redraft_pos
));
399 (*redraft_pos
)->offset
= strlen(dst
);
400 (*redraft_pos
)->hdrname
= cpystr(":");
412 * Do the if-else part of the detokenization for one case of if-else.
413 * The input src should like (match_this, if_matched, else)...
415 * Args expands_to -- This is what the token to the left of the paren
416 * expanded to, and this is the thing we're going to
417 * compare with the match_this part.
418 * src -- The source string beginning with the left paren.
421 * skip_ahead -- Tells caller how long the (...) part was so caller can
422 * skip over that part of the source.
424 * Returns -- an allocated string which is the answer, or NULL if nothing.
427 handle_if_token(char *expands_to
, char *src
, int for_what
, ENVELOPE
*env
,
428 ACTION_S
*role
, char **skip_ahead
)
432 char *match_this
, *if_matched
, *else_part
;
437 if(!src
|| *src
!= LPAREN
){
438 dprint((1,"botch calling handle_if_token, missing paren\n"));
443 q_status_message(SM_ORDER
, 3, 3,
444 "Unexpected end of token string in Reply-LeadIn, Sig, or template");
448 match_this
= get_token_arg(src
, &skip_to
);
451 * If the match_this argument is a token, detokenize it first.
453 if(match_this
&& *match_this
== '_'){
454 char *exp_match_this
;
456 exp_match_this
= detoken_src(match_this
, for_what
, env
,
458 fs_give((void **)&match_this
);
459 match_this
= exp_match_this
;
463 match_this
= cpystr("");
469 while(src
&& *src
&& (isspace((unsigned char)*src
) || *src
== ','))
472 if_matched
= get_token_arg(src
, &skip_to
);
474 while(src
&& *src
&& (isspace((unsigned char)*src
) || *src
== ','))
477 else_part
= get_token_arg(src
, &skip_to
);
479 while(src
&& *src
&& *src
!= RPAREN
)
482 if(src
&& *src
== RPAREN
)
488 if(!strcmp(match_this
, expands_to
)){
491 fs_give((void **)&else_part
);
496 fs_give((void **)&if_matched
);
499 fs_give((void **)&match_this
);
506 get_token_arg(char *src
, char **skip_to
)
508 int quotes
= 0, done
= 0;
509 char *ret
= NULL
, *p
;
511 while(*src
&& isspace((unsigned char)*src
)) /* skip space before string */
521 p
= ret
= (char *)fs_get((strlen(src
) + 1) * sizeof(char));
531 case BSLASH
: /* don't count \" as a quote, just copy */
532 if(*(src
+1) == BSLASH
|| *(src
+1) == QUOTE
){
533 src
++; /* skip backslash */