From the NannyMUD documentation

LAST CHANGE

2001-09-29

TOPIC

NAME

        C vs LPC - Summary of some differences C - LPC.

DESCRIPTION

	Here's a summary of the biggest differencies between C and Lpc.

	o Files are never compiled, the source files are read directly
	  into the driver.

	o There are no such thing as pointers.

	o You don't need a main(). In lpc functions are called directly
	  from the driver or when a command is typed.

	o There are many routines and functions that are hidden in the
          parser; equivalent to the kernal in UNIX, are basic system calls.

	o Strings are not arrays the way they are in C; they are much closer
	  to the strs of BASIC (you remember being forced to learn that trash
	  in High School, right?).  String functions are implied i.e. you may
	  us the '+' operator to combine strings such that:
		ack = foo+bar; (LPC) is equivalent to:
		strcpy(ack, foo); (C)
		strcat(ack, bar); (C)
	  Also ack[foo] does not refer to a character, it just return the
	  the integer value of the char at position foo.

	o LPC does not use typing.  this means that you can do nonsense like
	  this:

	  do_it()
	  {
		int i;

		i = "ack";
		return i;
	  }
	  This is of course not recommendable and the compiler will warn you
	  if you use #pragma strict_types.

	o The object orientedness of lpc is mostly an illusion. All you are
	  doing is modifing shared parameters, so don't expect C++ kinds of
	  stuff to work.

	o ++, --, +=, -=, *=, and /= can behave in strange ways, don't use
	  them in very complicated combinations.

	o All low- to high-level LPC objects are not held together like
  	  a traditional program; it is closer to writing a module for a large
	  program and using an incremental compiler upon each module
	  independant of all other modules.

	o Lpc is tokenised and interpreted, not compiled.

	o -> is not used for membership, in fact there are no such thing as
	  structs in LPC (but look at mappings).

NOTE

	What follows is a comment by Lars Pensjö on the new security system
	under driver 3.0 and it's derivative. NannyMUD, running in what is
	known as 'compat-mode', does not use this.

	In article <1991Sep21.170218.5667@bronze.ucs.indiana.edu>
	kellehe@silver.ucs.ini ana.edu (Mike Kelleher) writes:
	>
	> Most silly 3.0 questions, I hope there getting more intelligent as
	> we go along. Does anybody have a suggestions as a whole for security
	> measures in 3.0? There are several calls like Get_rootid that really
	> bothers me. Anyone got some suggestions?

	The name 'root' has nothing to do with the login name 'root' on unix
	systems. The only common thing is the spelling.

	The 3.0 has a new security system, which is not used if -o is
	specified !

	The new security system has been inspired by the Unix way of handling
	user id (uid) and effective user id (euid), but is not exactly the
	same.

	Every object will have a uid and a euid.  It is the euid that governs
	what permissions the object will have. The euid can be changed.

	The game driver only maintains these two values, represented as
	strings. It is the master.c that defines what they mean. The values can
	be any string, but one good way is to use the name of the wizard.

	When an object wants to access a file, valid_read (or valid_write) will
	be called in master.c, which will make a decision depending on the path
	name of the file and the euid. The 2.4.5 game driver called the
	valid_read in player.c, which was quite bad when there were no
	"current player".

	The euid can be changed to only some other values by the object, but
	always to the value of the uid. This is also defined by the master.c
	object.

	There is no set-uid as in Unix, but there is a possibility to transfer
	your euid to another objects uid. If object A do seteuid(0), then any
	object B can do export_uid(A) which will set the uid of A to the euid
	of B.

	There are some special rules (which I won't list here) which define
	what happens when an object makes another object become loaded. These
	rules are currently hard-coded in the game driver, but will probably
	also be transfered to master.c. In general, a loaded object either gets
	the same uid as the euid of the loader, or 0 (means no permission).

	This new security system is in many ways compatible with the old
	system. The big difference is that when a wizard A makes a room be
	loaded that was defined by wizard B, then you don't want that room to
	have uid A. Instead, it will have 0, which means that it must set the
	euid to something to be able to do any file access. This might seem
	arduos, but as all new mudlib systems are defined in a hierarchical
	way, a single line in a single file will (should :-) do it.

	There are some interesting problems resulting from this. For example,
	a player.c object can't call save_object() without preparations
	because it doesn't normally have permission to modifiy player save
	files.

	This is solved by talking with the master.c. The player object first
	sets its euid to 0, then calls a special request function in master.c
	which will give the player object the "root" uid. Now, the object can
	do the save, set back to euid 0 and return control to master.c. Of
	course, the master.c will validate that it is actually the player
	object, not something made by a wizard.

	Even if an object has euid "root", it can't access files outside the
	mudlib directory. This is never ever supposed to be possible.

	The wizlist modifications are now based on the uid of the object. That
	means that "root" can show up, as well as "backbone".

	Lars Pensj|
	lars@cd.chalmers.se

	::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

	Types declarations

	Types can be used at four places: Declaring type of global vari-
	ables.  Declaring type of functions.  Declaring type of arguments to
	functions.  Declaring type of local variables to functions.  Normally,
	the type information is completely ignored, and can be regarded purely
	sa documentation.  However, when the basic type of a function is
	declared, then a more strict type checking will be enforced.  That
	means that the type of all arguments must be defined.  And, the
	variables can only be used to store values of the declared type.  The
	function is defined to return an unkown type, as the compiler can't
	know the type.  This value must al- ways be casted (when strict type
	checking is enabled).  Casting a type is done by putting the type name
	inside a pair of '(' and An example when querying the short
	description of an object: (string)call_other(ob, "short");

	There are two kinds of types.  Basic types, and special types.  There
	can be at most one basic type, but any number of special types.  The
	strict type checking is only used by the compiler, not by the runtime.
	Hence, it is actually possible to store a number in a string variable
	even when strict type checking is en- abled.

	Why use strict type checking ?  It is really recommended, because the
	compiler will find many errors at compile time, which will save a lot
	of hard work.  It is in general much harder to trace an error occuring
	at run time.  I recommend, that when a wizard is having problem with
	an object and wants help, that he first must make all functions have
	declared types.


	Basic types

	An integer 32 bit number.  Pointer to an object.  An object pointer
	can mainly be used for two things.  Either giving as ar- gument to
	functions, or used for calling functions defined by that object with
	its specific instance of variables.  An unlimit- ed string of
	characters.  A lot of operators are allowed for strings, like and etc.
	This type is special, in that it is valid to use in any context.
	Thus, if everything was declared then the compiler would never
	complain.  This is of course not the idea.  It is really only supposed
	to be used when a variable really is going to contain different types
	of values.  This be avoided if possible.  It is good coding practice,
	to allow a function for example to return different types.  This type
	is only usable for functions.  It means that the function will not
	return any value.  The compiler will complain (when type checking is
	enabled) if the return value is used.


	Arrays

	Arrays are declared using a '*' with a basic type.  For example,
	declaring an array of numbers: "int *arr;".  Use the type if you want
	an array of arrays, or a mixed combination of types.


	Special types

	There are some special types, which can be given before the basic
	type.  These special types can also be combined.  When using spe- cial
	type before an statement, all symbols defined by inheritance will also
	get the special type The only special case is symbols, which can not
	be redefined as in a statement.  Can be given for both functions and
	variables.  Functions that are in object can not be called through
	from another object.  And, they are not ac- cessible to any object
	that inherits This special type behaves different for variables and
	functions.  It is similar to for functions, in that they can not be
	called from other objects.  variables will be neither saved nor
	restored when calling or A function defined as will always be
	accessible from other objects, even if is used.  All symbols defined
	as can not be redefined by inheritance.  They can still be used and
	accessed as usual.