1 #include "config.h"
2 
3 /*  Copyright (C) 2002  Brad Jorsch <anomie@users.sourceforge.net>
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <sys/types.h>
25 
26 #include "die.h"
27 #include "subst.h"
28 
29 #define GROW(var, len){{ \
30     void *c; \
31     len<<=1; \
32     if(len==0) len=1; \
33     if((var=realloc(c=var, len))==NULL){ \
34         out=c; \
35         warn("realloc error"); \
36         goto fail; \
37     } \
38 }}
39 
40 #define COPY(c) { \
41     if(k>=formatlen) GROW(format, formatlen); \
42     format[k++]=c; \
43 }
44 
subst(const char * s,struct subst_val * substitutes)45 char *subst(const char *s, struct subst_val *substitutes){
46     int i, j, k, n, m;
47     char *out=NULL;
48     size_t outlen=0;
49     char *format=NULL;
50     size_t formatlen=0;
51     int flags;
52     ssize_t str_start;
53 
54     for(i=j=0; s[i]!='\0'; i++){
55         if(s[i]!='%'){
56             if(j>=outlen) GROW(out, outlen);
57             out[j++]=s[i];
58             continue;
59         }
60         if(s[i+1]=='%'){
61             if(j>=outlen) GROW(out, outlen);
62             out[j++]=s[i++];
63             continue;
64         }
65 
66         n=i;
67         k=0;
68         COPY('%');
69 
70         /* skip flags */
71         flags=0;
72         while(strchr("#0- +'!", s[++n])){
73             if(s[n]=='!'){
74                 flags|=1;
75             } else {
76                 COPY(s[n]);
77             }
78         }
79 
80         /* min width? */
81         if(isdigit(s[n]) && s[n]!='0'){
82             COPY(s[n]);
83             while(isdigit(s[++n])){ COPY(s[n]); }
84         }
85 
86         /* precision? */
87         if(s[n]=='.'){
88             COPY('.');
89             while(isdigit(s[++n])){ COPY(s[n]); }
90         }
91 
92         str_start=0;
93         if(s[n]=='>'){
94             if(s[n+1]=='-'){
95                 flags|=2;
96                 n++;
97             }
98             while(isdigit(s[++n])){
99                 str_start=str_start*10+s[n]-'0';
100             }
101             if(flags&2) str_start=-str_start;
102         }
103 
104         for(m=0; s[n]!=substitutes[m].id && substitutes[m].id!='\0'; m++);
105         if(substitutes[m].id=='\0'){
106             warn("Unknown substitition character '%c' (at %d)\n", s[n], i);
107             goto fail;
108         }
109 
110         switch(substitutes[m].type){
111           case HEX:
112           case FLOAT_E:
113           case FLOAT_F:
114           case FLOAT_G:
115           case FLOAT_A:
116             if(flags&1){
117                 COPY(toupper(substitutes[m].type));
118                 break;
119             }
120             /* fall through*/
121 
122           default:
123             COPY(substitutes[m].type);
124             break;
125         }
126         COPY('\0');
127 
128 #define PRINT(var) { while((k=j+snprintf(out+j, outlen-j, format, var))>=outlen) GROW(out, outlen); j=k; }
129         switch(substitutes[m].type){
130           case INT:
131             PRINT(*(signed int *)substitutes[m].val);
132             break;
133 
134           case UINT:
135           case OCTAL:
136           case HEX:
137             PRINT(*(unsigned int *)substitutes[m].val);
138             break;
139 
140           case FLOAT_E:
141           case FLOAT_F:
142           case FLOAT_G:
143           case FLOAT_A:
144             PRINT(*(double *)substitutes[m].val);
145             break;
146 
147           case CHAR:
148             PRINT(*(char *)substitutes[m].val);
149             if(flags&1) out[j-1]=toupper(out[j-1]);
150             break;
151 
152           case STRING:
153             {
154                 char *s=*(char **)substitutes[m].val;
155                 if(str_start<0){
156                     str_start+=strlen(s);
157                     if(str_start<0) str_start=0;
158                 } else if(str_start>strlen(s)){
159                     s="";
160                     str_start=0;
161                 }
162                 s+=str_start;
163                 i=j;
164                 PRINT(s);
165                 if(flags&1){
166                     for(; i<j; i++) out[i]=toupper(out[i]);
167                 }
168             }
169             break;
170 
171           default:
172             warn("Unknown substitution type '%c'\n", substitutes[m].type);
173             goto fail;
174         }
175 
176         i=n;
177     }
178 
179     free(format);
180     if(j>=outlen) GROW(out, outlen);
181     out[j]='\0';
182     return out;
183 
184 fail:
185     free(format);
186     free(out);
187     return NULL;
188 }
189