File: bodyio.cc

package info (click to toggle)
cssc 1.0.1-4
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k, lenny
  • size: 3,620 kB
  • ctags: 1,424
  • sloc: cpp: 13,500; sh: 4,759; ansic: 2,971; perl: 342; makefile: 342; awk: 11
file content (451 lines) | stat: -rw-r--r-- 10,283 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
/*
 * bodyio.cc: Part of GNU CSSC.
 * 
 * 
 *    Copyright (C) 1997,1998,1999,2001 Free Software Foundation, Inc. 
 * 
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 * 
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 * 
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
 * 
 *
 * Code for performing I/O on the body of an SCCS file.
 * See also sf-admin.cc and encoding.cc.
 *
 */

#include "cssc.h"
#include "filepos.h"
#include "bodyio.h"
#include "sccsfile.h"
#include "linebuf.h"
#include "except.h"

#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif


/* Check if we have exceeded the maximum line length. 
 */
static bool check_line_len(const char *iname,
			   long int len_max,
			   int column,
			   int ch,
			   FILE *in,
			   bool *binary)
{
  if (0 == len_max || column < len_max)
    {
      return true;		// no maximum.
    }
  else
    {
      ungetc(ch, in);	// push back the character.
      
      if (binary_file_creation_allowed())
	{
	  errormsg("%s: line length exceeds %ld characters, "
		   "treating as binary.\n",
		   iname, len_max );
	  *binary = true;
	}
      else
	{
	  errormsg("%s: line length exceeds %d characters, "
		   "and binary file support is disabled.\n",
		   iname, len_max );
	}
      return false;	
    }
}




/* body_insert_text()
 *
 * Insert a file into an SCCS file (e.g. for admin).
 * return false (and restore initial file positions)
 * for failure.
 *
 * We fail if the input contains a ^A immediately following
 * a newline (which is special to SCCS), or if the input
 * does not end with a newline.
 *
 *  1) otherwise the control-character (^A) immediately
 *     following will not be recognised; SCCS utils
 *     only look for them at the beginning of a line
 * 
 *  2) many diff(1) programs only cope with text files
 *     that end with a newline.
 */
bool
body_insert_text(const char iname[], const char oname[],
		 FILE *in, FILE *out,
		 unsigned long int *lines,
		 bool *idkw,
		 bool *binary,
		 bool *io_failure)
{
  int ch, last;
  unsigned long int nl;		// number of lines.
  const long int len_max = max_sfile_line_len();
  bool found_id;
  int column;			// current column in ouutput file.
  
  // If we fail, rewind these files to try binary encoding.
  FilePosSaver o_saver(out);

  *idkw = found_id = false;
  nl = 0uL;
  last = '\n';
  *io_failure = false;

  // Make sure we don't already think it is binary -- if so, this 
  // function should never have been called.
  ASSERT(false == *binary);

  column = 0;
  while ( EOF != (ch=getc(in)) )
    {
      if (CONFIG_EOL_CHARACTER == ch)
	++nl;

      // check for ^A at start of line.
      if ('\n' == last)
	{
	  column = 0;
	  
	  if ('\001' == ch)
	    {
	      errormsg("%s: control character at start of line, "
		       "treating as binary.\n",
		       iname);
	      ungetc(ch, in);	// push back the control character.
	      *binary = true;
	      return false;	// output file pointer implicitly rewound
	    }
	}
      else
	{
	  ++column;
	  if (!check_line_len(iname, len_max, ++column, ch, in, binary))
	    {
	      return false; // output file pointer implicitly rewound
	    }
	}
      
      
      // FIXME TODO: if we get "disk full" while trying to write the
      // body of an SCCS file, we will retry with binary (which of
      // course uses even more space)
      if (putc_failed(putc(ch, out)))
	{
	  errormsg_with_errno("%s: Write error.", oname);
	  *io_failure = true;
	  return false;
	}

      if (!found_id)		// Check for ID keywords.
	{
	  if ('%' == last && is_id_keyword_letter(ch))
	    {
	      const int peek = getc(in);
	      if ('%' == peek)
		*idkw = found_id = true;
	      if (EOF != peek)	// Can't put the genie back in the bottle!
		ungetc(peek, in);
	    }
	}
      
      last = ch;
    }

  if (ferror(in))
    {
      errormsg_with_errno("%s: Read error.", iname);
      *io_failure = true;
      return false;
    }


  // Make sure the file ended with a newline.
  if ('\n' != last)
    {
      errormsg("%s: no newline at end of file, treating as binary\n",
	       iname);
      *binary = true;
      return false;		// file pointers implicitly rewound
    }

  // Success; do not rewind the output file.
  o_saver.disarm();
  *lines = nl;
  return true;
}

// Keywords: a file containing the octal characters 
// 0026 0021 0141 (hex 0x16 0x11 0x61) will produce
// begin 0644 x
// #%A%A
// `
// end
// 
//
// Stupidly imho, SCCS checks the ENCODED form of the file
// in order to support the "no ID keywords" warning.  Still,
// at least it doesn't expand those on output!


bool
body_insert_binary(const char iname[], const char oname[],
		   FILE *in, FILE *out,
		   unsigned long int *lines,
		   bool *idkw)
{
  const int max_chunk = 45;
  char inbuf[max_chunk], outbuf[80];
  unsigned long int nl;
  int len;
  bool kw;
  *idkw = kw = false;

  nl = 0;
  while ( 0 < (len = fread(inbuf, sizeof(char), max_chunk, in)) )
    {
      encode_line(inbuf, outbuf, len); // see encoding.cc.

      if (!kw)
	{
	  // For some odd reason, SCCS seems to check
	  // the encoded form for ID keywords!  We know
	  // that strlen() on the UUENCODEd data is safe.
	  if (check_id_keywords(outbuf, strlen(outbuf)))	// XXX used to check inbuf
	    *idkw = kw = true;
	}
      
      if (fputs_failed(fputs(outbuf, out)))
	{
	  errormsg_with_errno("%s: Write error.", oname);
	  return false;
	}
      
      ++nl;
    }
  // A space character indicates a count of zero bytes and hence
  // the end of the encoded file.
  if (fputs_failed(fputs(" \n", out)))
    {
      errormsg_with_errno("%s: Write error.", oname);
      return false;
    }
  
  ++nl;
  
  if (ferror(in))
    {
      errormsg_with_errno("%s: Read error.", iname);
      return false;
    }

  *lines = nl;
  return true;
}


bool
copy_data(FILE *in, FILE *out)
{
  char buf[BUFSIZ];
  size_t n, nout;

  while ( 0u < (n=fread(buf, 1, BUFSIZ, in)))
    {
      nout = fwrite(buf, 1, n, out);
      if (nout < n)
	{
	  errormsg_with_errno("copy_data: write error.");
	  return false;
	}
    }
  if (ferror(in))
    {
      errormsg_with_errno("copy_data: read error.");
      return false;
    }
  else
    {
      return true;		// success
    }
}


bool
body_insert(bool *binary,
	    const char iname[], const char oname[],
	    FILE *in, FILE *out,
	    unsigned long int *lines,
	    bool *idkw)
{
  // If binary mode has not been forced, try text mode.
  if (*binary)
    {
      return body_insert_binary(iname, oname, in, out, lines, idkw);
    }
  else
    {
      // body_insert_text() takes care of rewinding the output
      // file; we may not even be able to rewind the input file.
      bool io_failure = false;
      if (body_insert_text(iname, oname, in, out, lines, idkw, binary,
			   &io_failure))
	{
	  return true;		// Success.
	}
      else
	{
	  if (io_failure)
	    return false;

	  if (!binary_file_creation_allowed())
	    {
	      // We can't try again with a binary file, because that 
	      // feature is disabled.
	      return false;
	    }

	  // It wasn't text after all.  We may be reading from
	  // stdin, so we can't seek on it.  But we have 
	  // the first segment of the file written to the x-file
	  // already, and the remainder is still waiting to be
	  // read, so we can recover all the data.
	  *binary = true;
	  FILE *tmp = tmpfile();
	  if (tmp)
	    {
	      bool ret = true;
	      
#ifdef HAVE_EXCEPTIONS
	      try 
		{
#endif		  
		  // Recover the data already written to the output
		  // file and then rewind it, so that we can overwrite
		  // it with the encoded version.
		  FilePosSaver *fp_out = new FilePosSaver(out);
		  
		  if (copy_data(out, tmp) && copy_data(in,  tmp))
		    {
		      delete fp_out;	// rewind the file OUT.
		      rewind(tmp);
		      
		      ret = body_insert_binary("temporary file",
					       oname, tmp, out, lines, idkw);
		    }
		  else
		    {
		      ret = false;
		    }
#ifdef HAVE_EXCEPTIONS
		}
	      catch (CsscException)
		{
		  fclose(tmp);
		  throw;
		}
#endif
	      fclose(tmp);
	      return ret;
	    }
	  else
	    {
	      errormsg_with_errno("Could not create temporary file\n");
	      return false;
	    }
	}
    }
}

int output_body_line_text(FILE *fp, const cssc_linebuf* plb)
{
  return plb->write(fp) || fputc_failed(fputc('\n', fp));
}

int output_body_line_binary(FILE *fp, const cssc_linebuf* plb)
{
  // Curiously, if the file is encoded, we know that
  // the encoded form is only about 60 characters
  // and contains no 8-bit or zero data.
  size_t n;
  char outbuf[80];
  
  n = decode_line(plb->c_str(), outbuf); // see encoding.cc
  return fwrite_failed(fwrite(outbuf, sizeof(char), n, fp), n);
}


int 
encode_file(const char *nin, const char *nout)
{
  int retval = 0;
  
  FILE *fin = fopen_as_real_user(nin, "rb");	// binary
  if (0 == fin)
    {
      errormsg_with_errno("Failed to open \"%s\" for reading.\n", nin);
      return -1;
    }
  
//  FILE *fout = fopen(nout, "w"); // text
  FILE *fout = fcreate(nout, CREATE_EXCLUSIVE); // text

  if (0 == fout)
    {
      errormsg_with_errno("Failed to open \"%s\" for writing.\n", nout);
      retval = -1;
    }
  else
    {
#ifdef HAVE_EXCEPTIONS
      try
	{
#endif
	  encode_stream(fin, fout);
	  
	  if (ferror(fin) || fclose_failed(fclose(fin)))
	    {
	      errormsg_with_errno("%s: Read error.\n", nin);
	      retval = -1;
	    }
	  else
	    {
	      if (ferror(fout) || fclose_failed(fclose(fout)))
		{
		  errormsg_with_errno("%s: Write error.\n", nout);
		  retval = -1;
		}
	    }
#ifdef HAVE_EXCEPTIONS
	}
      catch (CsscException)
	{
	  remove(nout);
	  throw;
	}
#endif
      if (0 != retval)
	remove(nout);
    }
  return retval;
}