1 /*
2 * sf-prt.cc: Part of GNU CSSC.
3 *
4 * Copyright (C) 1997, 1998, 1999, 2001, 2004, 2007, 2008, 2009, 2010,
5 * 2011, 2014, 2019 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 * CSSC was originally Based on MySC, by Ross Ridge, which was
21 * placed in the Public Domain.
22 *
23 *
24 * Members of the class sccs_file for doing sccs-prt.
25 */
26
27
28 #include <config.h>
29
30 #include "cssc.h"
31 #include "sccsfile.h"
32 #include "seqstate.h"
33 #include "filepos.h"
34 #include "delta.h"
35 #include "delta-iterator.h"
36
37
38 #include <unistd.h> // SEEK_SET on SunOS.
39
40
41 template<class Container>
42 static void
print_string_list(FILE * out,const Container & l,const char * pre,const char * post,const char * dflt)43 print_string_list(FILE *out,
44 const Container& l,
45 const char* pre,
46 const char* post,
47 const char* dflt)
48 {
49 const std::vector<std::string>::size_type len = l.size();
50
51 if (0 == len)
52 {
53 fprintf(out, "%s%s", pre, dflt);
54 }
55 else
56 {
57 for (std::vector<std::string>::size_type i = 0; i < len; i++)
58 {
59 fprintf(out, "%s%s", pre, l[i].c_str());
60 if (i < len-1)
61 {
62 fprintf(out, "%s", post);
63 }
64
65 }
66 }
67 }
68
print_flag(FILE * out,const char * fmt,std::string flag,int & count)69 void print_flag(FILE *out, const char *fmt, std::string flag, int& count)
70 {
71 if (!flag.empty())
72 {
73 ++count;
74 fprintf(out, fmt, flag.c_str());
75 }
76 }
77
print_flag(FILE * out,const char * fmt,const std::string * pflag,int & count)78 void print_flag(FILE *out, const char *fmt, const std::string* pflag, int& count)
79 {
80 // We consider a flag which is set to an empty string still to be set.
81 // An example is the v flag; lines of the form "^Af v" should still set
82 // the v flag to an empty string.
83 if (pflag)
84 {
85 ++count;
86 fprintf(out, fmt, pflag->c_str());
87 }
88 }
89
print_flag(FILE * out,const char * fmt,int flag,int & count)90 void print_flag(FILE *out, const char *fmt, int flag, int& count)
91 {
92 if (flag)
93 {
94 ++count;
95 fprintf(out, fmt, flag ? "yes" : "no");
96 }
97 }
98
print_flag(FILE * out,const char * fmt,sid flag,int & count)99 void print_flag(FILE *out, const char *fmt, sid flag, int& count)
100 {
101 if (flag.valid())
102 {
103 ++count;
104 fprintf(out, "%s", fmt);
105 flag.print(out);
106 putc('\n', out);
107 }
108 }
109
print_flag(FILE * out,const char * fmt,release flag,int & count)110 void print_flag(FILE *out, const char *fmt, release flag, int& count)
111 {
112 if (flag.valid())
113 {
114 ++count;
115 fprintf(out, "%s", fmt);
116 flag.print(out);
117 fprintf(out, "\n");
118 }
119 }
120
excludes_delta(sid,sccs_date date,bool & stop_now) const121 bool sccs_file::cutoff::excludes_delta(sid /* s */,
122 sccs_date date,
123 bool& stop_now) const
124 {
125 stop_now = false;
126
127 if (!enabled)
128 return false;
129
130 if (first_accepted.valid())
131 {
132 // stop_now = stop_now || (date <= first_accepted);
133 if (date < first_accepted)
134 return true;
135 }
136 if (last_accepted.valid())
137 {
138 // Don't set stop_now, since we have not even got to the first
139 // one that's not excluded, yet.
140 if (date > last_accepted)
141 return true;
142 }
143 if (cutoff_delta)
144 {
145 stop_now = stop_now || (date <= cutoff_delta->date());
146 if (date < cutoff_delta->date())
147 return true;
148 }
149 return false;
150 }
151
152 void
print(FILE * out) const153 sccs_file::cutoff::print(FILE *out) const
154 {
155 fprintf(out, "cutoff: ");
156 if (enabled)
157 {
158 fputs("enabled ", out);
159 if (most_recent_sid_only)
160 fputs("most-recent-only ", out);
161 fputs("cutoff_sid='", out);
162 if (cutoff_sid.valid())
163 cutoff_sid.print(out);
164 else
165 fputs("(invalid)", out);
166
167 fputs("' first_accepted='", out);
168 if (first_accepted.valid())
169 {
170 first_accepted.printf(out, 'D');
171 fprintf(out, " ");
172 first_accepted.printf(out, 'T');
173 }
174 else
175 {
176 fputs("(invalid)'", out);
177 }
178 fputs("' last_accepted='", out);
179 if (last_accepted.valid())
180 {
181 last_accepted.printf(out, 'D');
182 fprintf(out, " ");
183 last_accepted.printf(out, 'T');
184 }
185 else
186 {
187 fputs("(invalid)'", out);
188 }
189
190 if (cutoff_delta)
191 {
192 fputs("' cutoff_delta->date='", out);
193 if (cutoff_delta->date().valid())
194 {
195 last_accepted.printf(out, 'D');
196 fprintf(out, " ");
197 last_accepted.printf(out, 'T');
198 }
199 else
200 {
201 fputs("(invalid)'", out);
202 }
203 }
204 fputs("'\n", out);
205 }
206 else
207 {
208 fputs("disabled\n", out);
209 }
210 }
211
cutoff()212 sccs_file::cutoff::cutoff()
213 : enabled(false), most_recent_sid_only(false),
214 cutoff_sid(sid()),
215 cutoff_delta(NULL)
216 {
217 // all done above.
218 }
219
220 // Print the body of an SCCS file, transforming all "^A"s
221 // into "*** "s.
222 static bool
do_print_body(const char * name,FILE * fp,long body_offset,FILE * out)223 do_print_body(const char *name, FILE *fp, long body_offset, FILE *out)
224 {
225 bool ret = true;
226
227 // When pos_saver goes out of scope the file position on "fp" is restored.
228 FilePosSaver pos_saver(fp);
229
230 if (0 != fseek(fp, body_offset, SEEK_SET))
231 {
232 errormsg_with_errno("%s: fseek() failed!", name);
233 return false; // can't read body now, so just fail.
234 }
235
236
237 if (putc_failed(putc('\n', out)))
238 ret = false;
239
240 int ch;
241 while ( ret && (ch=getc(fp)) != EOF )
242 {
243 if ('\001' == ch)
244 {
245 if (fputs_failed(fputs("*** ", out)))
246 {
247 ret = false; // write error
248 break;
249 }
250 }
251 else if ('\n' == ch)
252 {
253 int peek = getc(fp);
254
255 if ('\001' == peek)
256 {
257 ungetc(peek, fp);
258 if (putc_failed(putc('\n', out)))
259 ret = false;
260 }
261 else if (EOF == peek)
262 {
263 if (putc_failed(putc('\n', out)))
264 ret = false;
265 break;
266 }
267 else
268 {
269 ungetc(peek, fp);
270 if (fputs_failed(fputs("\n\t", out)))
271 {
272 ret = false; // write error
273 break;
274 }
275 }
276 }
277 else
278 {
279 if (putc_failed(putc(ch, out)))
280 {
281 ret = false; // write error
282 break;
283 }
284 }
285 }
286 if (ferror(fp)) // read error is fatal.
287 {
288 errormsg_with_errno("%s: read failed!", name);
289 ret = false;
290 }
291
292 // When pos_saver goes out of scope the file position is restored.
293 return ret;
294 }
295
296 static void
print_seq_list(FILE * out,std::vector<seq_no> const & list)297 print_seq_list(FILE *out, std::vector<seq_no> const &list)
298 {
299 bool first = true;
300 for (const auto& seq : list)
301 {
302 fprintf(out, "%s%u", (first ? "" : " "), seq);
303 first = false;
304 }
305 }
306
307
308 bool
prt(FILE * out,cutoff exclude,int all_deltas,int print_body,int print_delta_table,int print_flags,int incl_excl_ignore,int first_line_only,int print_desc,int print_users) const309 sccs_file::prt(FILE *out,
310 cutoff exclude, // -y, -c, -r
311 int all_deltas, // -a
312 int print_body, // -b
313 int print_delta_table, // -d
314 int print_flags, // -f
315 int incl_excl_ignore, // -i
316 int first_line_only, // -s
317 int print_desc, // -t
318 int print_users) const // -u
319
320 {
321 const int suppress_newlines = exclude.enabled;
322 const char* nl_sep = suppress_newlines ? " " : "\n";
323
324 if (print_delta_table)
325 {
326 if (exclude.enabled)
327 {
328 if (exclude.most_recent_sid_only)
329 {
330 find_most_recent_sid(exclude.cutoff_sid,
331 exclude.first_accepted);
332 }
333 if (exclude.cutoff_sid.valid())
334 exclude.cutoff_delta = find_delta(exclude.cutoff_sid);
335 }
336
337 bool stop_now = false;
338 const_delta_iterator iter(delta_table);
339
340 while (!stop_now && iter.next(all_deltas))
341 {
342 if (exclude.excludes_delta(iter->id(), iter->date(), stop_now))
343 continue;
344
345 // Unless -a was specified, don't print removed deltas.
346 if (!all_deltas && iter->removed())
347 continue;
348
349 if (exclude.enabled) // -y, -c, or -r option.
350 fprintf(out, "%s:\t", name.c_str());
351 else
352 putc('\n', out);
353
354 // Print the stuff from the delta...
355 fprintf(out, "%c ", iter->get_type());
356 iter->id().print(out);
357 putc('\t', out);
358 iter->date().printf(out, 'D');
359 putc(' ', out);
360 iter->date().printf(out, 'T');
361 fprintf(out, " %s\t%hu %hu",
362 iter->user().c_str(),
363 (unsigned short)iter->seq(),
364 (unsigned short)iter->prev_seq());
365 fprintf(out, "\t%05lu/%05lu/%05lu",
366 iter->inserted(), iter->deleted(), iter->unchanged());
367
368
369 if (!first_line_only)
370 {
371 if (incl_excl_ignore)
372 {
373 // TODO: find an elegant solution to this problem.
374 //
375 // REAL SCCS prints an "Included" line in the output
376 // if it sees "^Ai " and "an "Excluded" line if it
377 // sees "^Ax ", even if the rest of the lines was
378 // blank. For CSSC, the rest of these lines is
379 // built into a list of "seq_no"s. and so if the
380 // line is blank the data structure is empty and
381 // nothing is printed. This is a behavioural
382 // difference that to fix seems to require a real
383 // kludge.
384 if (!iter->get_included_seqnos().empty())
385 {
386 fputs(nl_sep, out); // either newline or space.
387 fprintf(out, "Included:\t");
388 print_seq_list(out, iter->get_included_seqnos());
389 }
390 else if (iter->has_includes())
391 {
392 fputs(nl_sep, out); // either newline or space.
393 fprintf(out, "Included:\t");
394 }
395 if (!iter->get_excluded_seqnos().empty())
396 {
397 fputs(nl_sep, out); // either newline or space.
398 fprintf(out, "Excluded:\t");
399 print_seq_list(out, iter->get_excluded_seqnos());
400 }
401 else if (iter->has_excludes())
402 {
403 fputs(nl_sep, out); // either newline or space.
404 fprintf(out, "Excluded:\t");
405 }
406 }
407 // Print any MRs and then the comments.
408 if (!iter->mrs().empty())
409 {
410 fputs(nl_sep, out); // either newline or space.
411 print_string_list(out, iter->mrs(), "MRs:\t", nl_sep, "");
412 }
413 if (!iter->comments().empty())
414 {
415 fputs(nl_sep, out); // either newline or space.
416 print_string_list(out, iter->comments(), "", nl_sep, "");
417 }
418 }
419 putc('\n', out);
420 }
421 }
422
423 // global stuff.
424 if (print_users)
425 {
426 fprintf(out, "\n Users allowed to make deltas -- \n");
427 print_string_list(out, users, "\t", "\n", "everyone");
428 putc('\n', out);
429 }
430
431 if (print_flags)
432 {
433 // PROBLEM:
434 //
435 // Those SCCS flags that have no "value" field, that is, those
436 // which are either on or off, have a tab printed immediately
437 // before their newlines...
438 //
439 // Except the "encoded" flag, which doesn't have a newline
440 // printed after it, for some reason.
441 //
442 // But we emulate even that bug :-)
443 //
444 int flag_count = 0;
445 fprintf(out, "\nFlags --\n");
446 if (flags.branch)
447 {
448 // "Real" SCCS prints a TAB after "branch", so we do too.
449 fprintf(out, "\tbranch\t\n");
450 ++flag_count;
451 }
452
453 print_flag(out, "\tceiling\t", flags.ceiling, flag_count);
454 print_flag(out, "\tdefault SID\t", flags.default_sid, flag_count);
455
456 // No newline after this one; odd, but it's what "real"
457 // SCCS does.
458 if (flags.encoded)
459 {
460 fprintf(out, "\tencoded");
461 ++flag_count;
462 }
463
464 #if 0
465 // The 'x' flag is a SCO OpenServer extension.
466 // SCO OpenServer 5.0.6 has no "prt" command, but
467 // SCO Unixware 8.0.0 does have it. However, it
468 // does not print anything if the x flag is set.
469 // Hence for compatibility, we don't do that either.
470 print_flag(out, "\texecutable\t\n", flags.executable, flag_count);
471 #endif
472
473 print_flag(out, "\tfloor\t", flags.floor, flag_count);
474 if (flags.no_id_keywords_is_fatal)
475 {
476 fprintf(out, "\tid_keywd err/warn\t\n");
477 ++flag_count;
478 }
479 if (flags.joint_edit)
480 {
481 fprintf(out, "\tjoint edit\t\n");
482 ++flag_count;
483 }
484 if (flags.all_locked)
485 {
486 fprintf(out, "\tlocked releases\ta\n");
487 ++flag_count;
488 }
489 else if (!flags.locked.empty())
490 {
491 fprintf(out, "\tlocked releases\t");
492 flags.locked.print(out);
493 putc('\n', out);
494 ++flag_count;
495 }
496 print_flag(out, "\tmodule\t%s\n", flags.module, flag_count);
497 print_flag(out, "\tnull delta\t\n", flags.null_deltas, flag_count);
498 print_flag(out, "\tcsect name\t%s\n", flags.user_def, flag_count);
499 print_flag(out, "\ttype\t%s\n", flags.type, flag_count);
500 print_flag(out, "\tvalidate MRs\t%s\n", flags.mr_checker, flag_count);
501
502 if (!flags.substitued_flag_letters.empty())
503 {
504 ++flag_count;
505
506 // Unusually, Solaris "prt" just launches into the list of
507 // expanded keyletters, without a leading flag name.
508 fprintf(out, "\t\t");
509 (void) print_subsituted_flags_list(out, " ");
510 fprintf(out, "\n");
511 }
512
513
514 if (0 == flag_count)
515 fprintf(out, "\tnone\n");
516 }
517 if (print_desc)
518 {
519 fprintf(out, "\nDescription --\n");
520 print_string_list(out, comments, "\t", "\n", "none");
521 putc('\n', out);
522 }
523
524 if (print_body)
525 {
526 // seek_to_body() is a non-const member, so we have this
527 // silly workaround.
528 do_print_body(name.c_str(), f, body_offset, out);
529 }
530
531 return true;
532 }
533
534
535 /* Local variables: */
536 /* mode: c++ */
537 /* End: */
538