/* -*-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 #include } #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 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); }