/* This is -*- C++ -*-.
   Copyright (C) 1992 Per Bothner.

This file is part of Q.

Q 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, or (at your option)
any later version.

Q 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 GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

// Certain prefixes of assembler-level labels have conventional
// usage that the linker (or a linker pre-processor) knows about.
// The ones we are concerned about start with "_$" followed
// by a two-letter code, followed by a more specific name.
// (Note that the prefix starts with '_' whether or not
// the C compiler prefixes identifiers y '_' - i.e. NAMES_HAVE_UNDERSCORE.)
// All "names" can be general strings that are encoded
// by encode_string_to_label (in encode.c).
// "_$ST" string_name  // A StringC string constant.
// "_$pk" package_name // The package with the given name.
// "_$SY" package_name "$$" print_name // A symbol in the named package.
// "_$KW" print_name // A Keyword - i.e. a Symbol in the Keyword package.
// "_$cl" print_name // A Symbol in the COMMON-LISP package.
// "_$sc" print_name // A Symbol in the SCHEME package.
// "_$QB" print_name // A Symbol in the BUILTIN package.
// Return NULL is the label has the wrong format.

#include "symbol.h"
#include "std.h"
extern "C" int encode_string_to_label(char *str, int length, char *buf);
extern "C" int decode_label_to_string(register char *str, char *buf);

Root *decode_label_to_object(char *label)
{
#ifdef __GNUC__
    char buf[strlen(label)];
#else
    char *buf = alloca(strlen(label));
#endif
    if (*label++ != '_' || *label++ != ENCODE_CHAR_2)
	return NULL;
    if (label[0] == 'S' && label[1] == 'T') { // A string.
	int len = decode_label_to_string(label+2, buf);
	return NewString(len, buf);
    }
    if (label[0] == 'P' && label[1] == 'K') { // A package.
	int len = decode_label_to_string(label+2, buf);
	return LookupPackage(buf, len);
    }
    if (label[0] == 'K' && label[1] == 'W') { // A keyword.
	int len = decode_label_to_string(label+2, buf);
	return KeywordPackage.intern(buf, len);
    }
    if (label[0] == 'c' && label[1] == 'l') { // A common-lisp symbol.
	int len = decode_label_to_string(label+2, buf);
	return CLispPackage.intern(buf, len);
    }
    if (label[0] == 's' && label[1] == 'c') { // A common-lisp symbol.
	int len = decode_label_to_string(label+2, buf);
	return SchemePackage.intern(buf, len);
    }
    if (label[0] == 'S' && label[1] == 'Y') { // A general symbol.
	label += 2;
	int len = decode_label_to_string(label+2, buf);
	label += len;
	Package *package = LookupPackage(buf, len);
	if (package == NULL) {
	    return NULL; // ???
	}
	// Look for two consecutive '$'s.
	while (label[0] != ENCODE_CHAR_2 || label[1] != ENCODE_CHAR_2)
	    if (label[0] == '\0') return NULL; // Error!
	    else label++;
	len = decode_label_to_string(label+2, buf);
	return CLispPackage.intern(buf, len);
    }
    return NULL;
}

// Place in buf a label encoding for the given Symbol.

extern "C" void encode_label_for_symbol(const Symbol *sym, char *buf)
{
    if (sym->_package == NULL)
	abort();
    char *label = buf;
    if (sym->_package == &KeywordPackage) {
	sprintf(buf, "_%cKW", ENCODE_CHAR_2);
	label += 4;
    }
    else if (sym->_package == &CLispPackage) {
	sprintf(buf, "_%ccl", ENCODE_CHAR_2);
	label += 4;
    }
    else if (sym->_package == &SchemePackage) {
	sprintf(buf, "_%csc", ENCODE_CHAR_2);
	label += 4;
    }
    else if (sym->_package == &BuiltinPackage) {
	sprintf(buf, "_%cQB", ENCODE_CHAR_2);
	label += 4;
    }
    else {
	const StringC *pack_name = sym->_package->_pname;
	sprintf(buf, "_%cSY", ENCODE_CHAR_2);
	encode_string_to_label(pack_name->chars(), pack_name->leng(),
			       buf+4);
	label = buf + strlen(buf);
	*label++ = ENCODE_CHAR_2;
	*label++ = ENCODE_CHAR_2;
    }
    encode_string_to_label(sym->string(), sym->length(), label);
}

// Return the size of a buffer that wouldbe guaranteed to be big
// enough for encode_label_for_symbol(sym, buffer).

extern "C" int encode_label_for_symbol_max(const Symbol* sym)
{
    int buf_size = sym->length();
    if (sym->_package)
	buf_size += sym->_package->_pname->leng();
    return 2*buf_size + 12;
}

extern "C" int encode_label_for_string_max(const StringC* sym)
{
    return 2*sym->leng() + 12;
}
