added a script to check language files

README updated
fatal error behaviour improved
failure behaviour for non-existing listdir changed
disable webusers textfield if the file does not exist
reduced list per column to 15
This commit is contained in:
lars 2005-12-25 15:01:46 +00:00
parent 5c20f5d3e6
commit 848f637cda
7 changed files with 178 additions and 125 deletions

View file

@ -123,40 +123,53 @@ ezmlm-web v3.0) from http://clearsilver.net.
IV. Files IV. Files
========= =========
In this distribution you should find eight files; In this distribution you should find the following files;
README This file. Provides some background information. README (optional) This file. Provides some background information.
Not needed to run ezmlm-web.
INSTALL Notes on installation. Not needed to run ezmlm-web. INSTALL (optional) Notes on installation.
CHANGES The change history. Not needed to run ezmlm-web INSTALL.clearsilver (optional) Notes on the installation of clearsilver.
TODO This file is a list of things I intend doing in future CHANGES (optional) The change history.
versions of ezmlm-web. Not needed to run ezmlm-web.
ezmlm-web.cgi The ezmlm-web script proper. This program requires that UPGRADING (optional) Notes on upgrading ezmlm-web from a previous version.
you have perl5 installed on your machine and that your web
server is capable of running CGI scripts.
index.c A C wrapper to allow ezmlm-web.cgi to run suid. Not TODO (optional) This file is a list of things I intend doing in future
strictly necessary if your setup allows perl scripts to versions of ezmlm-web.
run suid, but I prefer using wrappers anyway. It needs to
be edited and compiled to suit your system. Not needed to
run ezmlm-web.
ezmlmwebrc This is the configuration file for ezmlm-web. All options ezmlm-web.cgi (required) The ezmlm-web cgi script in perl. You will need it :)
are explained in this example file. You will need this
file to run ezmlm-web.
htaccess.sample A sample Apache .htaccess file for controlling access to index.c (recommended) A C wrapper to allow ezmlm-web.cgi to run
the mailing lists. If you use another web server, you will suid. Not strictly necessary if your setup allows perl scripts
have to work this bit out for yourself. to run suid, but I prefer using wrappers anyway. It needs to
be edited and compiled to suit your system. Not needed to
run ezmlm-web.
webusers.sample A sample webusers file for multi-level access control. ezmlmwebrc (required) This is the configuration file for ezmlm-web.
All options are explained in this example file. You will need
this file to run ezmlm-web.
The directory "lang" contains the language files. You will need at least the htaccess.sample (recommended) A sample Apache .htaccess file for controlling access to
one, that you have choosen in "ezmlmwebrc" by the option "HTML_LANGUAGE". the mailing lists. If you use another web server, you will
have to work this bit out for yourself.
webusers.sample (recommended) A sample webusers file for multi-level access control.
lang (directory, required) It contains the language files. You will need
at least the one, that you have choosen in "ezmlmwebrc" by
the option "HTML_LANGUAGE".
css (directory, recommended) Here you find available css stylesheet files.
Pick the one you like (for now, there is only one choice :)) and
set it up by pointing the "HTML_CSS_FILE" setting in your
"ezmlmwebrc" to an appropriate URL.
template (directory, required) The clearsilver template files in this
directory defined the layout of the web interface. You should be able
to adapt them to your needs, if you like.
spec (directory, optional) Some development specific information.
V. Notes V. Notes
@ -188,30 +201,27 @@ of constraints come into place.
- The webusers file is scanned for either the list name (case insensitive) or - The webusers file is scanned for either the list name (case insensitive) or
an ALL (case sensitive) entry. an ALL (case sensitive) entry.
- The list entry (or ALL) is scanned for the current user (as set in - The list entry (or ALL) is scanned for the current user (as set in
$REMOTE_USER) or an ALL entry. $REMOTE_USER) or an ALL (user) entry.
- If any valid match is made, then the user is allowed to edit the list. - If any valid match is made, then the user is allowed to edit the list.
Otherwise the user is politely told to go away ;-) Otherwise the user is politely told to go away ;-)
If list creation is allowed and the webusers file exists, then the person who If list creation is allowed and the webusers file exists, then the person who
creates the list is the default owner. As of yet there is no way to create users creates the list is the default owner. There is no way to create users
through the web interface, but I intend to do this eventually. through the web interface, as this depends on your authentication system.
The format of a webusers file is as follows; The format of a webusers file is as follows;
list1: user1, user2, user3 list1: user1 user2 user3
ALL: user1, user2 ALL: user1 user2
list2: ALL list2: ALL
ie; listname colon (:) and a comma (,) separated list of users. Spaces are
ignored but each list must appear on a new line.
Once this file exists, the ezmlm-web script will allow the list users to Once this file exists, the ezmlm-web script will allow the list users to
configure their access lists along with any other options. configure their access lists along with any other options.
You can permit some users to create lists by adding a line similar to the You can permit some users to create lists by adding a line similar to the
following to your webusers file: following to your webusers file:
ALLOW_CREATE: user2, user3 ALLOW_CREATE: user2 user3
If there is no line starting with "ALLOW_CREATE:" in the webusers file, then If there is no line starting with "ALLOW_CREATE:" in the webusers file, then
no one will be allowed to create lists. This behaviour is new for ezmlm-web no one will be allowed to create lists. This behaviour is new for ezmlm-web

View file

@ -96,7 +96,7 @@ if(defined($opt_C)) {
} elsif(-e "/etc/ezmlm/ezmlmwebrc") { } elsif(-e "/etc/ezmlm/ezmlmwebrc") {
require "/etc/ezmlm/ezmlmwebrc"; # System require "/etc/ezmlm/ezmlmwebrc"; # System
} else { } else {
die "Unable to read config file"; &fatal_error("Unable to read config file");
} }
# Allow suid wrapper to over-ride default list directory ... # Allow suid wrapper to over-ride default list directory ...
@ -119,7 +119,7 @@ if (defined($MAIL_DOMAIN) && ($MAIL_DOMAIN ne '')) {
$DEFAULT_HOST = $MAIL_DOMAIN; $DEFAULT_HOST = $MAIL_DOMAIN;
} else { } else {
# Work out default domain name from qmail (for David Summers) # Work out default domain name from qmail (for David Summers)
open (GETHOST, "<$QMAIL_BASE/defaultdomain") || open (GETHOST, "<$QMAIL_BASE/me") || die "Unable to read $QMAIL_BASE/me: $!"; open (GETHOST, "<$QMAIL_BASE/defaultdomain") || open (GETHOST, "<$QMAIL_BASE/me") || &fatal_error("Unable to read $QMAIL_BASE/me: $!");
chomp($DEFAULT_HOST = <GETHOST>); chomp($DEFAULT_HOST = <GETHOST>);
close GETHOST; close GETHOST;
} }
@ -267,9 +267,15 @@ elsif ($action eq '' || $action eq 'list_select') {
$error = 'UnknownAction'; $error = 'UnknownAction';
} }
# read the current state # read the current state (after the changes are done)
&set_pagedata(); &set_pagedata();
# set default action, if there is no list available and the user is
# allowed to create a new one
if (($action eq '') && (&webauth_create_allowed()) && ($pagedata->getValue('Data.Lists.0','') eq '')) {
$pagename = 'list_create';
}
# Print page and exit :) ... # Print page and exit :) ...
&output_page; &output_page;
exit; exit;
@ -284,7 +290,9 @@ sub load_hdf {
$hdf->readFile($LANGUAGE_DIR . '/' . $HTML_LANGUAGE . '.hdf'); $hdf->readFile($LANGUAGE_DIR . '/' . $HTML_LANGUAGE . '.hdf');
# TODO: check for existence # TODO: check for existence
&fatal_error("Template dir ($TEMPLATE_DIR) not found!") unless (-e $TEMPLATE_DIR);
$hdf->setValue("TemplateDir", "$TEMPLATE_DIR/"); $hdf->setValue("TemplateDir", "$TEMPLATE_DIR/");
&fatal_error("Language data dir ($LANGUAGE_DIR) not found!") unless (-e $LANGUAGE_DIR);
$hdf->setValue("LanguageDir", "$LANGUAGE_DIR/"); $hdf->setValue("LanguageDir", "$LANGUAGE_DIR/");
$hdf->setValue("ScriptName", $ENV{'SCRIPT_NAME'}); $hdf->setValue("ScriptName", $ENV{'SCRIPT_NAME'});
$hdf->setValue("Stylesheet", "$HTML_CSS_FILE"); $hdf->setValue("Stylesheet", "$HTML_CSS_FILE");
@ -306,8 +314,8 @@ sub output_page {
$pagedata->setValue('Data.Action', "$pagename"); $pagedata->setValue('Data.Action', "$pagename");
my $pagefile = $TEMPLATE_DIR . "/main.cs"; my $pagefile = $TEMPLATE_DIR . "/main.cs";
die "main template ($pagefile) not found!" unless (-e "$pagefile"); &fatal_error("main template ($pagefile) not found!") unless (-e "$pagefile");
die "sub template ($TEMPLATE_DIR/$pagename.cs) not found!" unless (-e "$TEMPLATE_DIR/$pagename.cs"); &fatal_error("sub template ($TEMPLATE_DIR/$pagename.cs) not found!") unless (-e "$TEMPLATE_DIR/$pagename.cs");
# print http header # print http header
print "Content-Type: text/html; charset=utf-8\n\n"; print "Content-Type: text/html; charset=utf-8\n\n";
@ -319,80 +327,86 @@ sub output_page {
print $cs->render(); print $cs->render();
} }
# ---------------------------------------------------------------------------
sub set_pagedata_list_of_lists()
{
my (@files, $i, $num);
# Read the list directory for mailing lists.
return (0==0) unless (opendir DIR, $LIST_DIR);
@files = sort grep !/^\./, readdir DIR;
closedir DIR;
$num = 0;
# Check that they actually are lists and add good ones to pagedata ...
foreach $i (0 .. $#files) {
if ((-e "$LIST_DIR/$files[$i]/lock") && (&webauth($files[$i]))) {
$pagedata->setValue("Data.Lists." . $num, "$files[$i]");
$num++;
}
}
}
# ---------------------------------------------------------------------------
sub set_pagedata() sub set_pagedata()
{ {
my (@lists, @files, $i, $item); my ($hostname, $username);
# Read the list directory for mailing lists. # read available list of lists
unless (opendir DIR, $LIST_DIR) { &set_pagedata_list_of_lists();
$warning = 'ListDirAccessDenied';
return (1==0); # username and hostname
# Work out if this user has a virtual host and set input accordingly ...
if(-e "$QMAIL_BASE/virtualdomains") {
open(VD, "<$QMAIL_BASE/virtualdomains") || warn "Can't read virtual domains file: $!";
while(<VD>) {
last if(($hostname) = /(.+?):$USER/);
}
close VD;
} }
if(!defined($hostname)) {
@files = sort grep !/^\./, readdir DIR; $username = "$USER-" if ($USER ne $ALIAS_USER);
closedir DIR; $hostname = $DEFAULT_HOST;
}
# Check that they actually are lists and add good ones to pagedata ... $pagedata->setValue("Data.UserName", "$username");
my $num = 0; $pagedata->setValue("Data.HostName", "$hostname");
foreach $i (0 .. $#files) {
if ((-e "$LIST_DIR/$files[$i]/lock") && (&webauth($files[$i]))) {
$pagedata->setValue("Data.Lists." . $num, "$files[$i]");
$num++;
}
}
# list specific configuration
if ($q->param('list') ne '' )
{
&set_pagedata4list(&get_list_part());
} else {
&set_pagedata4options($DEFAULT_OPTIONS);
}
# username and hostname # modules
my ($hostname, $username); # TODO: someone should test, if the mysql support works
# Work out if this user has a virtual host and set input accordingly ... $pagedata->setValue("Data.Modules.MySQL", ($Mail::Ezmlm::MYSQL_BASE)? 1 : 0);
if(-e "$QMAIL_BASE/virtualdomains") {
open(VD, "<$QMAIL_BASE/virtualdomains") || warn "Can't read virtual domains file: $!";
while(<VD>) {
last if(($hostname) = /(.+?):$USER/);
}
close VD;
}
if(!defined($hostname)) {
$username = "$USER-" if ($USER ne $ALIAS_USER);
$hostname = $DEFAULT_HOST;
}
$pagedata->setValue("Data.UserName", "$username");
$pagedata->setValue("Data.HostName", "$hostname");
# modules
# TODO: someone should test, if the mysql support works
$pagedata->setValue("Data.Modules.MySQL", ($Mail::Ezmlm::MYSQL_BASE)? 1 : 0);
# permissions # permissions
$pagedata->setValue("Data.Permissions.Create", (&webauth_create_allowed)? 1 : 0 ); $pagedata->setValue("Data.Permissions.Create", (&webauth_create_allowed)? 1 : 0 );
$pagedata->setValue("Data.Permissions.FileUpload", ($FILE_UPLOAD)? 1 : 0); $pagedata->setValue("Data.Permissions.FileUpload", ($FILE_UPLOAD)? 1 : 0);
# display webuser textfield? # display webuser textfield?
$pagedata->setValue("Data.WebUser.show", (-e "$WEBUSERS_FILE")? 1 : 0); $pagedata->setValue("Data.WebUser.show", (-e "$WEBUSERS_FILE")? 1 : 0);
# default username for webuser file # default username for webuser file
$pagedata->setValue("Data.WebUser.UserName", $ENV{'REMOTE_USER'}||'ALL'); $pagedata->setValue("Data.WebUser.UserName", $ENV{'REMOTE_USER'}||'ALL');
# list specific configuration
if ($q->param('list') ne '' )
{
&set_pagedata4list(&get_list_part());
} else {
&set_pagedata4options($DEFAULT_OPTIONS);
}
} }
# ---------------------------------------------------------------------------
sub set_pagedata4list sub set_pagedata4list
{ {
my $part_type = shift; my $part_type = shift;
my ($list, $listname, $webusers); my ($list, $listname, $webusers);
my ($i, $item, @files); my ($i, $item, @files);
my ($address, $addr_name, %pretty);
$listname = $q->param('list'); $listname = $q->param('list');
@ -406,14 +420,11 @@ sub set_pagedata4list
$pagedata->setValue("Data.List.Name", "$listname"); $pagedata->setValue("Data.List.Name", "$listname");
$pagedata->setValue("Data.List.Address", &this_listaddress); $pagedata->setValue("Data.List.Address", &this_listaddress);
&set_pagedata4part_list($part_type) if ($part_type ne ''); &set_pagedata4part_list($part_type) if ($part_type ne '');
$i = 0; $i = 0;
my $address;
my $addr_name;
my %pretty;
tie %pretty, "DB_File", "$LIST_DIR/$listname/webnames" if ($PRETTY_NAMES); tie %pretty, "DB_File", "$LIST_DIR/$listname/webnames" if ($PRETTY_NAMES);
# TODO: use "pretty" output style for visible mail address
foreach $address (sort $list->subscribers($part_type)) { foreach $address (sort $list->subscribers($part_type)) {
if ($address ne '') { if ($address ne '') {
$pagedata->setValue("Data.List.Subscribers." . $i . '.address', "$address"); $pagedata->setValue("Data.List.Subscribers." . $i . '.address', "$address");
@ -468,20 +479,17 @@ sub set_pagedata4list
if (opendir DIR, "$listDir/text") { if (opendir DIR, "$listDir/text") {
@files = grep !/^\./, readdir DIR; @files = grep !/^\./, readdir DIR;
closedir DIR; closedir DIR;
$i = 0;
foreach $item (@files) {
$pagedata->setValue("Data.List.Files." . $i, "$item");
$i++;
}
} else { } else {
$warning = 'TextDirAccessDenied' if ($warning eq '') $warning = 'TextDirAccessDenied' if ($warning eq '')
} }
$i = 0;
my $item;
foreach $item (@files) {
$pagedata->setValue("Data.List.Files." . $i, "$item");
$i++;
}
# text file specified? # text file specified?
if ($q->param('file') ne '') if (($q->param('file') ne '') && ($q->param('file') =~ m/^[\w-]*$/)) {
{
my ($content); my ($content);
$content = $list->getpart("text/" . $q->param('file')); $content = $list->getpart("text/" . $q->param('file'));
from_to($content,$TEXT_ENCODE,'utf8'); # by ooyama for multibyte from_to($content,$TEXT_ENCODE,'utf8'); # by ooyama for multibyte
@ -623,7 +631,7 @@ sub delete_list {
$warning = 'UnsafeRemoveListDirFailed'; $warning = 'UnsafeRemoveListDirFailed';
return (1==0); return (1==0);
} }
opendir(DIR, "$HOME_DIR") or die "Unable to get directory listing: $!"; opendir(DIR, "$HOME_DIR") or &fatal_error("Unable to get directory listing: $!");
my @files = map { "$HOME_DIR/$1" if m{^(\.qmail.+)$} } grep { /^\.qmail-$listaddress/ } readdir DIR; my @files = map { "$HOME_DIR/$1" if m{^(\.qmail.+)$} } grep { /^\.qmail-$listaddress/ } readdir DIR;
closedir DIR; closedir DIR;
if (unlink(@files) <= 0) { if (unlink(@files) <= 0) {
@ -832,7 +840,7 @@ sub create_list {
# Check if the list directory exists and create if necessary ... # Check if the list directory exists and create if necessary ...
unless ((-e $LIST_DIR) || (mkdir $LIST_DIR, 0700)) { unless ((-e $LIST_DIR) || (mkdir $LIST_DIR, 0700)) {
warn "Unable to create directory ($LIST_DIR): $!"; warn "Unable to create directory ($LIST_DIR): $!";
$error = 'ListDirUnavailable'; $warning = 'ListDirAccessDenied';
return (1==0); return (1==0);
} }
@ -1111,7 +1119,6 @@ sub save_text {
my ($list) = new Mail::Ezmlm("$LIST_DIR/" . $q->param('list')); my ($list) = new Mail::Ezmlm("$LIST_DIR/" . $q->param('list'));
my ($content) = $q->param('content'); my ($content) = $q->param('content');
# TODO: is "utf8" instead of "utf-8" correct?
from_to($content,'utf8',$TEXT_ENCODE); # by ooyama for multibyte from_to($content,'utf8',$TEXT_ENCODE); # by ooyama for multibyte
unless ($list->setpart("text/" . $q->param('file'), $content)) { unless ($list->setpart("text/" . $q->param('file'), $content)) {
$warning = 'SaveFile'; $warning = 'SaveFile';
@ -1211,6 +1218,19 @@ sub rmtree {
# ------------------------------------------------------------------------ # ------------------------------------------------------------------------
sub fatal_error() {
my $text = shift;
print "Content-Type: text/html; charset=utf-8\n\n";
print "<html><head>\n";
print "<title>ezmlm-web</title></head>\n";
print "<body><h1>a fatal error occoured!</h1>\n";
print "<p><strong><big>$text</big></strong></p>\n";
print "<p>check the error log of your web server for details</p>\n";
print "</body></html>\n";
die "$text";
}
# ------------------------------------------------------------------------ # ------------------------------------------------------------------------
# End of ezmlm-web.cgi v2.3 # End of ezmlm-web.cgi v2.3
# ------------------------------------------------------------------------ # ------------------------------------------------------------------------

View file

@ -1,9 +1,7 @@
#$Id: htaccess.sample,v 1.1 2000/01/29 11:35:40 guy Exp $
#
#order deny,allow #order deny,allow
#deny from all #deny from all
#allow from .ru.ac.za #allow from .ru.ac.za
AuthName "EZ Mailing List Manager AuthName "EZ Mailing List Manager"
AuthType Basic AuthType Basic
AuthUserFile /etc/ezmlm/.htusers AuthUserFile /etc/ezmlm/.htusers
require valid-user require valid-user

View file

@ -61,8 +61,7 @@ Lang {
ErrorMessage { ErrorMessage {
UnknownAction = Diese Aktion ist undefiniert! UnknownAction = Diese Aktion ist undefiniert!
ParameterMissing = Diese Aktion benätigt weitere Parameter! ParameterMissing = Diese Aktion benätigt weitere Parameter!
Forbidden = Fehler: dir fehlen die notwendigen Rechte für diese Aktion Forbidden = Fehler: dir fehlen die notwendigen Rechte für diese Aktion
ListDirUnavailable = Fehler beim Zugriff auf das Hauptverzeichnis der Listen!
InvalidFileName = Der Dateiname ist nicht zulässig. InvalidFileName = Der Dateiname ist nicht zulässig.
UnknownConfigPage = Die gähle Konfigurations-Seite existiert nicht! UnknownConfigPage = Die gähle Konfigurations-Seite existiert nicht!
} }
@ -166,6 +165,7 @@ Lang {
AcceptanceAddress = die Zustimmungsadresse AcceptanceAddress = die Zustimmungsadresse
RejectionAddress = die Ablehnungsadresse RejectionAddress = die Ablehnungsadresse
} }
SuggestDefaultPath = Ansonsten musst du diese Mitglieder per Hand verwalten.
PostModPathWarn = Die Nachrichten-ModeratorInnen werden nicht an ihrem üblichen Ort gespeichert. PostModPathWarn = Die Nachrichten-ModeratorInnen werden nicht an ihrem üblichen Ort gespeichert.
SubModPathWarn = Die Einschreibe-ModeratorInnen werden nicht an ihrem üblichen Ort gespeichert. SubModPathWarn = Die Einschreibe-ModeratorInnen werden nicht an ihrem üblichen Ort gespeichert.
RemoteAdminPathWarn = Die Fern-AdministratorInnen werden nicht an ihrem üblichen Ort gespeichert. RemoteAdminPathWarn = Die Fern-AdministratorInnen werden nicht an ihrem üblichen Ort gespeichert.

View file

@ -61,8 +61,7 @@ Lang {
ErrorMessage { ErrorMessage {
UnknownAction = this action is undefined UnknownAction = this action is undefined
ParameterMissing = this action needs one or more parameters ParameterMissing = this action needs one or more parameters
Forbidden = Error: you are not allowed to do this! Forbidden = Error: you are not allowed to do this!
ListDirUnavailable = Could not create the list directory!
InvalidFileName = The name of the file is invalid! InvalidFileName = The name of the file is invalid!
UnknownConfigPage = The chosen config page is invalid! UnknownConfigPage = The chosen config page is invalid!
} }
@ -78,9 +77,9 @@ Lang {
ListNameAlreadyExists = There is already a list with this name! ListNameAlreadyExists = There is already a list with this name!
ListAddressAlreadyExists = There is already a list with this address! ListAddressAlreadyExists = There is already a list with this address!
ListDoesNotExist = A list with this name does not exist! ListDoesNotExist = A list with this name does not exist!
ListDirAccessDenied = Unable to access the list's directory: ListDirAccessDenied = Unable to access the list's directory
TextDirAccessDenied = Unable to access the list's directory of text files: TextDirAccessDenied = Unable to access the list's directory of text files
SafeRemoveRenameDirFailed = Unable to rename list for safe removal: SafeRemoveRenameDirFailed = Unable to rename list for safe removal
DotQmailDirAccessDenied = Unable to read the mail user's home directory for .qmail files DotQmailDirAccessDenied = Unable to read the mail user's home directory for .qmail files
SafeRemoveMoveDotQmailFailed = Unable to move .qmail files SafeRemoveMoveDotQmailFailed = Unable to move .qmail files
UnsafeRemoveListDirFailed = Unable to delete list UnsafeRemoveListDirFailed = Unable to delete list
@ -152,7 +151,7 @@ Lang {
ListAddress = List Address ListAddress = List Address
ListOptions = Basic List Options ListOptions = Basic List Options
AllowedToEdit = Users allowed to edit this list via web interface: AllowedToEdit = Users allowed to edit this list via web interface:
HeaderRemove = Headers to strip from all outgoing mail HeaderRemove = Headers to strip from every outgoing mail
HeaderAdd = Headers to add to all outgoing mail HeaderAdd = Headers to add to all outgoing mail
MimeRemove = Mime types to strip from all outgoing mail MimeRemove = Mime types to strip from all outgoing mail
MimeReject = Messages containing any of these mime type will be rejected MimeReject = Messages containing any of these mime type will be rejected

View file

@ -0,0 +1,26 @@
#!/bin/sh
#
# compare the defined fields of a language file with the english translation
#
# nice for finding unavailable definitions
#
# Parameter: LANGUAGE
# (e.g. "de")
#
set -u
LANG_DIR=$(dirname $0)/../lang
DEFAULT_LANG=en
TMP_FILE1=/tmp/$(basename $0)-$$-1
TMP_FILE2=/tmp/$(basename $0)-$$-2
[ $# -ne 1 ] && echo -e "Syntax: $(basename $0) LANGUAGE\n" >&2 && exit 1
grep "=" "$LANG_DIR/${DEFAULT_LANG}.hdf" | grep -v "^[[:space:]]*#" | cut -f 1 -d "=" >"$TMP_FILE1"
grep "=" "$LANG_DIR/${1}.hdf" | grep -v "^[[:space:]]*#" | cut -f 1 -d "=" >"$TMP_FILE2"
diff -wu "$TMP_FILE1" "$TMP_FILE2"
rm "$TMP_FILE1" "$TMP_FILE2"

View file

@ -1,5 +1,5 @@
comm: guy, arb comm: guy arb
users: arb users: arb
members: ALL members: ALL
ALL: root ALL: root
ALLOW_CREATE: root, guy ALLOW_CREATE: root guy