1 /*
2  * writesubst.cc: Part of GNU CSSC.
3  *
4  *  Copyright (C) 2001, 2004, 2007, 2008, 2009, 2010, 2011, 2014, 2019
5  *  Free Software Foundation, Inc.
6  *
7  *  This program is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  *
21  * sccsfile::write_subst()
22  *
23  */
24 
25 #include <config.h>
26 #include <string>
27 
28 #include "cssc.h"
29 #include "sccsfile.h"
30 #include "delta.h"
31 #include "ioerr.h"
32 
33 // #include "pfile.h"
34 // #include "seqstate.h"
35 // #include "delta-iterator.h"
36 // #include "delta-table.h"
37 
38 
39 #include <ctype.h>
40 
41 using std::string;
42 
43 /* Return TRUE if the specified keyword letter should be
44  * expanded in the gotten file.  If the y flag is set, it controls the
45  * keyletters which are expanded.  If the y flag is not present, all
46  * key letters are expanded.  The y flag is a Solaris 8 extension.
47  */
expand_keyletter(char which,const std::set<char> & expanded)48 static bool expand_keyletter(char which, const std::set<char>& expanded)
49 {
50   if (expanded.empty())
51     return true;
52   else
53     return expanded.find(which) != expanded.end();
54 }
55 
56 
57 /* Write a line of a file after substituting any id keywords in it.
58    Returns true if an error occurs. */
59 
60 int
write_subst(const char * start,struct subst_parms * parms,const delta & d,bool force_expansion) const61 sccs_file::write_subst(const char *start,
62                        struct subst_parms *parms,
63                        const delta& d,
64 		       bool force_expansion) const
65 {
66   FILE *out = parms->out;
67 
68   const char *percent = strchr(start, '%');
69   while (percent != NULL)
70     {
71       char c = percent[1];
72       if (c != '\0' && percent[2] == '%')
73         {
74           if (start != percent
75               && fwrite(start, percent - start, 1, out) != 1)
76             {
77               return 1;
78             }
79 
80           int err = 0;
81 	  if (!force_expansion
82 	      && false == expand_keyletter(c, flags.substitued_flag_letters))
83 	    {
84 	      // We do not expand this key letter.   Just emit the raw
85 	      // characters.
86 	      err = fputc_failed(fputc('%', out))
87 		||  fputc_failed(fputc(c,   out))
88 		||  fputc_failed(fputc('%', out));
89 
90 	      if (err)
91 		{
92 		  return 1;
93 		}
94 	      else
95 		{
96 		  start = percent+3;
97 		  percent = strchr(start, '%');
98 		  continue;
99 		}
100 	    }
101           percent += 3;
102 
103 
104 
105 	  // We need to expand the keyletter.
106           switch (c)
107             {
108               const char *s;
109 
110             case 'M':
111               {
112                 const char *mod = get_module_name().c_str();
113                 err = fputs_failed(fputs(mod, out));
114               }
115             break;
116 
117             case 'I':
118               err = d.id().print(out);
119               break;
120 
121             case 'R':
122               err = d.id().printf(out, 'R', 1);
123               break;
124 
125             case 'L':
126               err = d.id().printf(out, 'L', 1);
127               break;
128 
129             case 'B':
130               err = d.id().printf(out, 'B', 1);
131               break;
132 
133             case 'S':
134               err = d.id().printf(out, 'S', 1);
135               break;
136 
137             case 'D':
138               err = parms->now.printf(out, 'D');
139               break;
140 
141             case 'H':
142               err = parms->now.printf(out, 'H');
143               break;
144 
145             case 'T':
146               err = parms->now.printf(out, 'T');
147               break;
148 
149             case 'E':
150               err = d.date().printf(out, 'D');
151               break;
152 
153             case 'G':
154               err = d.date().printf(out, 'H');
155               break;
156 
157             case 'U':
158               err = d.date().printf(out, 'T');
159               break;
160 
161             case 'Y':
162               if (flags.type)
163                 {
164                   err = fputs_failed(fputs(flags.type->c_str(), out));
165                 }
166               break;
167 
168             case 'F':
169               err =
170                 fputs_failed(fputs(base_part(name.sfile()).c_str(),
171                                    out));
172               break;
173 
174             case 'P':
175               if (1) // introduce new scope...
176                 {
177                   string path(canonify_filename(name.c_str()));
178                   err = fputs_failed(fputs(path.c_str(), out));
179                 }
180               break;
181 
182             case 'Q':
183               if (flags.user_def)
184                 {
185                   err = fputs_failed(fputs(flags.user_def->c_str(), out));
186                 }
187               break;
188 
189             case 'C':
190               err = printf_failed(fprintf(out, "%u",
191                                           parms->out_lineno));
192               break;
193 
194             case 'Z':
195               if (fputc_failed(fputc('@', out))
196                   || fputs_failed(fputs("(#)", out)))
197                 {
198                   err = 1;
199                 }
200               else
201                 {
202                   err = 0;
203                 }
204               break;
205 
206             case 'W':
207               s = parms->wstring;
208               if (0 == s)
209                 {
210                   /* At some point I had been told that SunOS 4.1.4
211                    * apparently uses a space rather than a tab here.
212                    * However, a test on 4.1.4 shows otherwise.
213                    *
214                    * From: "Carl D. Speare" <carlds@attglobal.net>
215                    * Subject: RE: SunOS 4.1.4
216                    * To: 'James Youngman' <jay@gnu.org>,
217                    *         "cssc-users@gnu.org" <cssc-users@gnu.org>
218                    * Date: Wed, 11 Jul 2001 01:07:36 -0400
219                    *
220                    * Ok, here's what I got:
221                    *
222                    * %W% in a file called test.c expanded to:
223                    *
224                    * @(#)test.c<TAB>1.1
225                    *
226                    * Sorry, but my SunOS machine is lacking a network
227                    * connection, so I can't bring it over into
228                    * mail-land. But, there you are, for what it's
229                    * worth.
230                    *
231                    * --Carl
232                    *
233                    */
234                   s = "%Z" "%%M" "%\t%" "I%";
235                   /* NB: strange formatting of the string above is
236                    * to preserve it unchanged even if this source code does
237                    * itself get checked into SCCS or CSSC.
238                    */
239                 }
240               else
241                 {
242                   /* protect against recursion */
243                   parms->wstring = 0;
244                 }
245               err = write_subst(s, parms, d, true);
246               if (0 == parms->wstring)
247                 {
248                   parms->wstring = s;
249                 }
250               break;
251 
252             case 'A':
253               err = write_subst("%Z""%%Y""% %M""% %I"
254                                 "%%Z""%",
255                                 parms, d, true);
256               break;
257 
258             default:
259               start = percent - 3;
260               percent = percent - 1;
261               continue;
262             }
263 
264           parms->found_id = 1;
265 
266           if (err)
267             {
268               return 1;
269             }
270           start = percent;
271         }
272       else
273         {
274           percent++;
275         }
276       percent = strchr(percent, '%');
277     }
278 
279   return fputs_failed(fputs(start, out));
280 }
281