373 строки
14 KiB
Plaintext
373 строки
14 KiB
Plaintext
This document
|
|
=============
|
|
|
|
This document is a guide how to develop GNU Midnight Commander. It's
|
|
quite incomplete, but may be worth reading anyway.
|
|
|
|
The document was written by Miguel de Icaza and reworked by Pavel
|
|
Roskin. Some parts were taken from the messages posted in the mailing
|
|
lists.
|
|
|
|
|
|
Compiling from CVS
|
|
==================
|
|
|
|
To compile GNU Midnight commander from CVS, the following software is
|
|
required:
|
|
|
|
Autoconf 2.52 and above (latest is recommended)
|
|
Automake 1.5 and above (latest is recommended)
|
|
Gettext 0.11.5 and above
|
|
Glib 1.2.6 and above (2.x is recommended)
|
|
|
|
It is recommended that all those tools are installed with the same
|
|
prefix. Make sure that the tools with the right version are first in
|
|
PATH.
|
|
|
|
Once you have the right tools, run `autogen.sh' - it will generate
|
|
everything necessary for the build and run `configure'. Arguments given
|
|
to `autogen.sh' are passed to `configure'. Then run `make' as usually.
|
|
|
|
The distribution tarball is created by the command `make distcheck'.
|
|
This command can take a while.
|
|
|
|
Currently snapshots are made on Debian unstable and use the versions of
|
|
the tools from the unstable repository. Yes, the rpm packages are made
|
|
on Debian too.
|
|
|
|
Note that the version of gettext doesn't affect the snapshot because the
|
|
distributed files are installed by gettext from archives for the version
|
|
used in the AM_GNU_GETTEXT_VERSION macro, which is 0.11.5.
|
|
|
|
|
|
|
|
Working with GNU Midnight Commander
|
|
===================================
|
|
|
|
Please use the CVS version. It may be quite different from the released
|
|
versions. A lot of cleanup is going on. The CVS version may be easier
|
|
to understand, in addition to the obvious fact that the merging is
|
|
easier with the CVS version.
|
|
|
|
There are some tools in the maint directory on CVS. They are not
|
|
included with releases or snapshots. You may be interested to look at
|
|
them if you are going to work on the project for an extended period of
|
|
time. In particular, the release procedure is described there.
|
|
|
|
In order to compile GNU Midnight Commander from a clean CVS checkout you
|
|
should use autogen.sh instead of configure. Arguments passed to
|
|
autogen.sh are passed to configure after it's generated.
|
|
|
|
GNU Midnight Commander uses Autoconf and Automake, with make it fairly
|
|
portable. However, GNU Make is strongly recommended for development
|
|
because other versions of make may not track dependencies properly.
|
|
This is very important for correct compilation, especially if you change
|
|
any header files.
|
|
|
|
If you add or remove any files, please change Makefile.am in the same
|
|
directory accordingly. When doing significant changes in the tree
|
|
structure, "make distcheck" is strongly recommended.
|
|
|
|
If you have etags installed, you can run "make tags" and use tags in
|
|
emacs to find functions or variables. But you can also use the internal
|
|
editor and the "Find File" command to find any text in the source tree.
|
|
|
|
GNU Autoconf allows you to test several different configurations are
|
|
once. To do so, use the so called out-of-tree (or VPATH) compilation.
|
|
Create separate empty directories and run configure with full path from
|
|
those directories, like this:
|
|
|
|
cd /usr/local/src
|
|
mkdir mc-slang
|
|
mkdir mc-ncurses
|
|
cd mc-slang
|
|
/usr/local/src/mc/configure && make all
|
|
cd ../mc-ncurses
|
|
/usr/local/src/mc/configure --with-screen=ncurses && make all
|
|
|
|
Please use the same indentation as other developers. To indent a block,
|
|
select in the internal editor and use Shift-F9 to call the external
|
|
indent. For historic reasons, GNU Midnight Commander used formatting
|
|
that is not default for GNU Indent. Please put following text to your
|
|
~/.indent.pro file to make GNU Indent follow the style used in GNU
|
|
Midnight Commander:
|
|
|
|
-kr -i4 -pcs -psl --ignore-newlines
|
|
|
|
It's OK to indent the whole function if you edit it. However, please
|
|
refrain from it if you are posting your patch for review. In this case
|
|
you would save time of other developers if you only include significant
|
|
changes. The developer applying your patch can format the code for you.
|
|
|
|
Please keep in mind that the VFS subsystem is licensed under LGPL, while
|
|
the rest of the code uses GPL.
|
|
|
|
|
|
Code structure - outline
|
|
========================
|
|
|
|
The code is located in following directories.
|
|
|
|
vfs - Virtual File System.
|
|
|
|
This library provides filesystem-like access to various data, such are
|
|
archives and remote filesystems. To use VFS, you should use wrappers
|
|
around POSIX calls. The wrappers have names composed from "mc_" and the
|
|
standard name of the function. For example, to open a file on VFS, use
|
|
mc_open() instead.
|
|
|
|
edit - the internal editor.
|
|
|
|
This code has been contributed by Paul Sheer, the author of Cooledit.
|
|
The internal editor shares some code with Cooledit, but now it's
|
|
developed as part of GNU Midnight Commander.
|
|
|
|
src - the main part of the code.
|
|
|
|
This code includes the dialog manager written by Radek Doulik and source
|
|
code of the main application.
|
|
|
|
slang - stripped down S-Lang library.
|
|
|
|
It's provided to allow compilation that don't have the S-Lang library
|
|
with complete headers or the library is broken. Please avoid changing
|
|
this code. If you do change it, please consider contributing your
|
|
changes to the maintainers of S-Lang.
|
|
|
|
|
|
Code structure - details
|
|
========================
|
|
|
|
GNU Midnight Commander uses extensively the dialog manager written by
|
|
Radek Doulik. To understand how the dialog manager works, please read
|
|
the dialog.c. You will find the basic widgets in the files widget.c.
|
|
Some more high-level functions, e.g. to display a message box, are
|
|
located in wtools.c. This file also contains the Quick Dialog code,
|
|
which makes it easier to create complex dialogs.
|
|
|
|
Files findme.c, popt.c, poptconfig.c, popthelp.c and poptparse.c come
|
|
from the popt library used to parse the command line. They should not
|
|
be modified unless absolutely necessary.
|
|
|
|
The files util.c and utilunix.c have a lot of utility functions. Get
|
|
familiar with them, they are very simple.
|
|
|
|
glib is used for memory allocation and for some utility functions, such
|
|
as manipulation with lists and trees. gmodule (part of the glib
|
|
distribution) is used to load some libraries dynamically at the run
|
|
time.
|
|
|
|
Thanks to glib, the code has almost no hardcoded limits, since there are
|
|
many ways to avoid them. For example, when you want to concatenate
|
|
strings, use the g_strconcat() function:
|
|
|
|
new_text = g_strconcat (username, " ", password, NULL);
|
|
|
|
This allocates new memory for the string, so you should use g_free() on
|
|
the result.
|
|
|
|
The parent of all dialogs is called midnight_dlg. Both panels are
|
|
widgets in that dialog. Other widgets include the menu, the command
|
|
line and the button bar.
|
|
|
|
|
|
Input handling
|
|
==============
|
|
|
|
The routines for input handling on the Midnight Commander are:
|
|
getch, get_key_code, mi_getch and get_event.
|
|
|
|
getch is an interface to the low level system input mechanism. It
|
|
does not deal with the mouse.
|
|
|
|
In the case of ncurses, this is a function implemented in the
|
|
ncurses library that translates key sequences to key codes (\E[A to
|
|
something like KEY_UP and so on).
|
|
|
|
In the case of S-Lang there is no such conversion, that's why we
|
|
load a set of extra definitions.
|
|
|
|
The get_key_code routine converts the data from getch to the
|
|
constants the Midnight Commander uses.
|
|
|
|
In the case of S-Lang, it will actually do all the jobs that getch
|
|
does for curses. In the case of curses it patches a couple of
|
|
sequences that are not available on some terminal databases. This
|
|
routine is the one you want to use if you want a character without
|
|
the mouse support.
|
|
|
|
get_event is the routine you want to use if you want to handle mouse
|
|
events, it will return 0 on a mouse event, -1 if no input is available
|
|
or a key code if there is some input available. This routine in turn
|
|
uses get_key_code to decode the input stream and convert it to useful
|
|
constants.
|
|
|
|
mi_getch is just a wrapper around get_event that ignores all the mouse
|
|
events. It's used only in a couple of places, this routine may return
|
|
-1 if no input is available (if you have set the nodelay option of
|
|
ncurses or S-Lang with nodelay) or a character code if no such option is
|
|
available.
|
|
|
|
|
|
Mouse support
|
|
=============
|
|
|
|
The mouse support in the Midnight Commander is based on the get_event
|
|
routine. The core of the mouse event dispatching is in the
|
|
dlg.c:run_dlg routine.
|
|
|
|
|
|
ncurses
|
|
=======
|
|
|
|
Although S-Lang is now used by default, we still support ncurses. We
|
|
basically are using a small subset of ncurses because we want to be
|
|
compatible with Slang.
|
|
|
|
|
|
The Dialog manager and the Widgets
|
|
==================================
|
|
|
|
The Dialog manager and the Widget structure are implemented in
|
|
src/dialog.c. Everything shown on screen is a dialog. Dialogs contain
|
|
widgets, but not everything on screen is a widget. Dialogs can draw
|
|
themselves.
|
|
|
|
Dialogs are connected into a singly linked list using "parent" field.
|
|
Currently active dialog is saved in current_dlg variable. The toplevel
|
|
dialog has parent NULL. Usually it's midnight_dlg.
|
|
|
|
parent parent
|
|
current_dlg ------->another dialog-- ... -->midnight_dlg
|
|
|
|
When the screen needs to be refreshed, every dialog asks its parent to
|
|
refresh first, and then refreshes itself.
|
|
|
|
A dialog is created by create_dlg(). Then it's populated by widgets
|
|
using add_widget(). Then the dialog is run by calling run_dlg(), which
|
|
returns the id of the button selected by the user. Finally, the dialog
|
|
is destroyed by calling destroy_dlg().
|
|
|
|
Widgets are placed to a doubly linked circular list. Each widget has
|
|
previous and next widget.
|
|
|
|
prev next prev next
|
|
widget1 <---------> widget2 <---------> widget3
|
|
^ ^
|
|
-----------------------------------------
|
|
next prev
|
|
|
|
Pressing Tab moves focus to the "next" widget, pressing Shift-Tab moves
|
|
focus to "prev". The tab order is equal to the add order except some
|
|
old code that use the reverse order by setting DLG_REVERSE flag in
|
|
create_dlg() call. Please don't use reverse order in the new code.
|
|
|
|
The initial widget to get focus can be selected by calling
|
|
dlg_select_widget().
|
|
|
|
When creating a dialog, you may want to use a callback that would
|
|
intercept some dialog events. However, many widgets will do the right
|
|
thing by default, so some dialogs can work just fine without callbacks.
|
|
|
|
There are also widget events, which are sent by the dialog to individual
|
|
widgets. Some widgets also have user callbacks.
|
|
|
|
To create your own widget, use init_widget(). In this case, you must
|
|
provide a callback function. Please note that it's not the same as the
|
|
user callback in some widgets.
|
|
|
|
|
|
Where to Find Bug Reports and Patches
|
|
=====================================
|
|
|
|
There are various sources where bug reports and patches can be found.
|
|
|
|
http://savannah.gnu.org/bugs/?group=mc
|
|
The primary bug tracking system for MC.
|
|
|
|
http://bugs.debian.org/mc
|
|
The bug tracking system for Debian, a package collection mainly
|
|
for GNU/Linux and the Hurd.
|
|
|
|
http://bugzilla.redhat.com/bugzilla/buglist.cgi?component=mc
|
|
Bugs reported in Redhat Linux.
|
|
|
|
http://savannah.gnu.org/patch/?group=mc
|
|
Patches reported in the GNU Savannah project.
|
|
|
|
http://www.openbsd.org/cgi-bin/cvsweb/ports/misc/mc/patches/
|
|
The patches that are applied for the OpenBSD version of MC.
|
|
|
|
http://www.freebsd.org/cgi/cvsweb.cgi/ports/misc/mc/files/
|
|
The patches that are applied for the FreeBSD version of MC.
|
|
|
|
http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/sysutils/mc/patches/
|
|
The patches that are applied for the NetBSD version of MC.
|
|
|
|
http://cvs.sourceforge.net/viewcvs.py/pkgsrc-wip/wip/mc/patches/
|
|
The patches for the work-in-progress NetBSD package of MC.
|
|
|
|
http://www.gentoo.org/cgi-bin/viewcvs.cgi/app-misc/mc/files/?hideattic=1
|
|
The patches that are applied for the Gentoo Linux version of MC.
|
|
|
|
|
|
Programming Tips
|
|
================
|
|
|
|
(This list should be sorted alphabetically.)
|
|
|
|
const: For every function taking a string argument, decide whether you
|
|
(as a user of the function) would expect that the string is modi-
|
|
fied by the function. If not, declare the string argument as
|
|
"const char *". If your implementation needs to modify the string,
|
|
use g_strdup to create a local copy.
|
|
|
|
const_cast: We use many libraries that do not know about "const char *" and
|
|
thus declare their functions to require "char *". In cases where we
|
|
know the function does not modify the string, we can use the macro
|
|
const_cast(char *, string_var) instead of a simple (char *) string_var
|
|
to make the intent clear. In cases where we are not sure what the
|
|
function does with the string, we should use g_strdup to pass
|
|
dynamically allocated strings.
|
|
|
|
g_free: g_free handles NULL argument too, no need for the comparison.
|
|
Bad way:
|
|
if (old_dir) g_free (old_dir);
|
|
Right way:
|
|
g_free (old_dir);
|
|
|
|
g_strdup: When you use g_strdup to create a local copy of a string, use
|
|
the following pattern to keep the reference.
|
|
|
|
char * const pathref = g_strdup(argument);
|
|
/* ... */
|
|
g_free (pathref);
|
|
|
|
The "const" will make the pointer unmodifiable (pathref++
|
|
is not possible), but you can still modify the string contents.
|
|
|
|
g_strlcpy: Whenever you use this function, be sure to add "glibcompat.h"
|
|
to the included headers. This is because in glib-1.2 there is
|
|
no such function.
|
|
|
|
NULL: When you pass NULL as an argument of a varargs function, cast the
|
|
NULL to the appropriate data type. If a system #defines NULL to
|
|
be 0 (at least NetBSD and OpenBSD do), and the sizes of int and
|
|
a pointer are different, the argument will be passed as int 0,
|
|
not as a pointer.
|
|
|
|
This tip applies at least to catstrs (edit/edit.h), execl(3),
|
|
execle(3), execlp(3), g_strconcat (glib), parent_call
|
|
(src/background.h), parent_call_string (src/background.h),
|
|
rpc_get (vfs/mcfsutil.h), rpc_send (vfs/mcfsutil.h).
|
|
|
|
example:
|
|
char *path = g_strconcat("dir", "/", "file", (char *) NULL);
|
|
|
|
size_t: This data type is suitable for expressing sizes of memory or the
|
|
length of strings. This type is unsigned, so you need not check
|
|
if the value is >= 0.
|
|
|
|
strncpy: Don't use this function in newly created code. It is slow, insecure
|
|
and hard to use. A much better alternative is g_strlcpy (see there).
|