/* -*-Mode: C++;-*-
* PRCS - The Project Revision Control System
* Copyright (C) 1997 Josh MacDonald
* Copyright (C) 1994 P. N. Hilfinger
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: setkeys.cc 1.4.1.5.1.8.1.2.1.9 Wed, 24 Oct 2001 03:23:43 -0700 jmacd $
*/
extern "C" {
#include <unistd.h>
#include <fcntl.h>
}
#include "prcs.h"
#include "setkeys.h"
#include "memseg.h"
#include "system.h"
#include "projdesc.h"
#include "fileent.h"
static ostream* output_file; /* non-null if outputting to a file. */
static MemorySegment *output_segment; /* non-null if outputting to memory. */
static int deletekeys; /* true iff unsetting keys. */
static KeywordTable* keywords;
static KeywordTable* keywords_replacing;
static const char* unchanged_segment;
static int unchanged_len;
static bool output_changed;
static PrConstCharPtrError process_normal_key(const char* inp, long len, bool simple, bool no_dollars);
typedef Pair <const char*, const char*> StringPair;
static const char* find_keyword(const char* s, int len, int* old_len, const char** key)
{
int i;
for (i = 0; i < len; i += 1) {
if (s[i] == '$' || s[i] == ':')
break;
}
static Dstring* buf = NULL;
if (!buf)
buf = new Dstring;
buf->assign (s, i);
*old_len = buf->length();
StringPair* lu = keywords->lookup_pair (buf->cast());
if (lu) {
(*key) = lu->x();
return lu->y();
} else
return NULL;
}
static PrVoidError checked_write(const char* s, int len)
{
if(output_file) {
output_file->write(s, len);
if (output_file->bad())
pthrow prcserror << "Write failed while setting keys" << perror;
}
if (output_segment)
output_segment->append_segment(s, len);
return NoError;
}
static PrVoidError output_text_segment(const char* s, long len)
{
if (s == NULL) {
/* nothing */
} else if (len == 0) {
return NoError;
} else if(unchanged_segment+unchanged_len == s ||
memcmp(unchanged_segment+unchanged_len, s, len) == 0) {
unchanged_len += len;
return NoError;
} else {
output_changed = true;
}
if (unchanged_len > 0) {
Return_if_fail(checked_write(unchanged_segment, unchanged_len));
unchanged_segment += unchanged_len;
unchanged_len = 0;
}
if (s != NULL)
Return_if_fail(checked_write(s, len));
return NoError;
}
static PrVoidError output_simple_key_replaced (const char* format, int format_len, bool no_dollars)
{
while (format_len > 0) {
switch (*format) {
case '\\':
switch (format[1]) {
case 't':
Return_if_fail(output_text_segment("\t", 1));
break;
default:
Return_if_fail(output_text_segment(format+1, 1));
break;
}
format += 2; format_len -= 2;
break;
case '$': {
const char* end_phrase;
Return_if_fail(end_phrase << process_normal_key(format+1, format_len-1, true, no_dollars));
if (end_phrase == NULL) {
Return_if_fail(output_text_segment(no_dollars ? "|" : format, 1));
format += 1; format_len -= 1;
} else {
format_len -= end_phrase - format;
format = end_phrase;
}
break;
}
default:
Return_if_fail(output_text_segment(format, 1));
format += 1; format_len -= 1;
break;
}
}
return NoError;
}
/* Assuming that inp is positioned just after a '$' that might begin */
/* a keyword phrase other than Format, convert the phrase and send to */
/* output_text_segment, returning a pointer to just after the closing */
/* '$' in the input. If this is not the start of a keyword phrase, */
/* return NULL. If SIMPLE, then just output the keyword's value, */
/* without any surrounding keyword:...$ text. */
static PrConstCharPtrError process_normal_key(const char* inp, long len, bool simple, bool no_dollars)
{
const char* end_phrase;
const char* after_key;
int new_len;
const char* replace;
const char* current_key;
int key_len;
end_phrase = (const char*)memchr(inp, '$', len);
if (end_phrase == NULL || memchr(inp, '\n', end_phrase - inp) != NULL)
return (const char*)0;
end_phrase += 1;
replace = find_keyword(inp, len, &key_len, ¤t_key);
if (replace) {
after_key = inp + key_len;
new_len = strlen(replace);
if ( deletekeys ) {
Return_if_fail(output_text_segment(inp, key_len));
Return_if_fail(output_text_segment("$", 1));
} else if (keywords_replacing->lookup (current_key)) {
return (const char*)0;
} else {
if (! simple) {
Return_if_fail(output_text_segment(inp, key_len));
Return_if_fail(output_text_segment(": ", 2));
}
keywords_replacing->insert (current_key, current_key);
Return_if_fail (output_simple_key_replaced (replace, new_len, no_dollars));
keywords_replacing->remove (current_key);
if (! simple)
Return_if_fail(output_text_segment(" $", 2));
}
return end_phrase;
} else {
return (const char*)0;
}
}
/* Assuming that inp is positioned just after a '$' before the string */
/* "Format:", convert the phrase (with the subsequent line) and send to */
/* output_text_segment, returning a pointer to just after the replaced */
/* line in the input. If this is not the start of a proper Format phrase, */
/* return NULL. */
static PrConstCharPtrError process_format(const char* inp0, long len0)
{
const char* inp;
const char* format;
const char* end_format_line;
int len, format_len;
inp = inp0; len = len0;
inp += 7; len -= 7;
while (isspace(*inp) && len > 0)
inp += 1, len -= 1;
if (*inp != '\"')
return (const char*)0;
format = inp += 1;
len -= 1;
while(true) {
if (len == 0 || *inp == '\n')
return (const char*)0;
else if (*inp == '\"')
break;
else if (*inp == '\\' && len > 1)
inp += 2, len -= 2;
else
inp += 1, len -= 1;
}
format_len = inp - format;
do {
inp += 1, len -= 1;
} while (len > 0 && *inp != '$' && *inp != '\n');
if (len == 0 || *inp != '$')
return (const char*)0;
do {
inp += 1; len -= 1;
} while (len > 0 && *inp != '\n');
if (len <= 0)
return (const char*)0;
end_format_line = inp;
do {
inp += 1; len -= 1;
} while (len > 0 && *inp != '\n');
Return_if_fail (output_text_segment(inp0, end_format_line - inp0 + 1));
if ( deletekeys )
return inp;
Return_if_fail (output_simple_key_replaced (format, format_len, false));
return inp;
}
/* Convert input[0 .. inputLen-1] according to keyword_number and */
/* keyword_pair_list as indicated in the comments at the beginning of this */
/* file, sending the results to output_text_segment. */
static PrVoidError convert(const char* input_buffer, int input_buffer_len)
{
output_changed = false;
unchanged_len = 0;
unchanged_segment = input_buffer;
while(true) {
const char* end_phrase;
const char* next_possible_key;
if (output_changed && !output_file && !output_segment)
return NoError;
next_possible_key = (const char*)memchr(input_buffer, '$', input_buffer_len);
if (next_possible_key == NULL) {
Return_if_fail(output_text_segment(input_buffer, input_buffer_len));
break;
}
next_possible_key += 1;
Return_if_fail(output_text_segment(input_buffer, next_possible_key - input_buffer));
input_buffer_len -= next_possible_key - input_buffer;
input_buffer = next_possible_key;
if (strncmp(input_buffer, "Format:", 7) == 0)
Return_if_fail(end_phrase << process_format(input_buffer, input_buffer_len));
else
Return_if_fail(end_phrase << process_normal_key(input_buffer, input_buffer_len, false, true));
if (end_phrase == NULL)
continue;
input_buffer_len -= end_phrase - input_buffer;
input_buffer = end_phrase;
}
return output_text_segment(NULL, 0);
}
/*****/
static MemorySegment buffer(false);
PrBoolError setkeys_internal(const char* input_buffer0,
int input_buffer_len0,
ostream* os,
MemorySegment* seg,
FileEntry *fe,
SetkeysAction action)
{
KeywordTable replace_table;
keywords = fe->project()->project_keywords (fe, action == Setkeys);
deletekeys = action == Unsetkeys;
output_segment = seg;
output_file = os;
keywords_replacing = &replace_table;
if (seg) seg->clear_segment();
Return_if_fail(convert(input_buffer0, input_buffer_len0));
return output_changed;
}
PrBoolError setkeys_infile(const char* desc,
int fd,
int length,
const char* file1, FileEntry *fe,
SetkeysAction action)
{
Return_if_fail (buffer.map_file(desc, fd, length));
ofstream os(file1);
Return_if_fail(setkeys_inputbuf(buffer.segment(), buffer.length(), &os, fe, action));
os.close();
if (os.bad())
pthrow prcserror << "Write failed on file " << squote(file1) << perror;
return output_changed;
}
PrBoolError setkeys(const char* file0, const char* file1, FileEntry *fe,
SetkeysAction action)
{
Return_if_fail (buffer.map_file (file0));
ofstream os(file1);
Return_if_fail(setkeys_inputbuf(buffer.segment(), buffer.length(), &os, fe, action));
os.close();
if (os.bad())
pthrow prcserror << "Write failed on file " << squote(file1) << perror;
return output_changed;
}
PrBoolError setkeys_inputbuf(const char* input_buffer0,
int input_buffer_len0,
ostream* os,
FileEntry *fe,
SetkeysAction action)
{
return setkeys_internal(input_buffer0, input_buffer_len0, os, NULL, fe, action);
}
PrBoolError setkeys_inoutbuf(const char* inputbuf,
int inputbuflen,
MemorySegment* seg,
FileEntry *fe,
SetkeysAction action)
{
return setkeys_internal(inputbuf, inputbuflen, NULL, seg, fe, action);
}
PrBoolError setkeys_outbuf(const char* file,
MemorySegment* seg,
FileEntry *fe,
SetkeysAction action)
{
Return_if_fail(buffer.map_file(file));
return setkeys_internal(buffer.segment(), buffer.length(), NULL, seg, fe, action);
}
syntax highlighted by Code2HTML, v. 0.9.1