Annotation of loncom/ConfigFileEdit.pm, revision 1.4

1.1       foxr        1: #
                      2: #
1.4     ! albertel    3: # $Id: lond,v 1.378 2007/08/08 22:24:36 albertel Exp $
1.1       foxr        4: #
                      5: # Copyright Michigan State University Board of Trustees
                      6: #
                      7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      8: #
                      9: # LON-CAPA is free software; you can redistribute it and/or modify
                     10: # it under the terms of the GNU General Public License as published by
                     11: # the Free Software Foundation; either version 2 of the License, or
                     12: # (at your option) any later version.
                     13: #
                     14: # LON-CAPA is distributed in the hope that it will be useful,
                     15: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     16: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     17: # GNU General Public License for more details.
                     18: #
                     19: # You should have received a copy of the GNU General Public License
                     20: # along with LON-CAPA; if not, write to the Free Software
                     21: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     22: #
                     23: # /home/httpd/html/adm/gpl.txt
                     24: #
                     25: # http://www.lon-capa.org/
                     26: #
                     27: 
                     28: package ConfigFileEdit;
                     29: 
1.2       foxr       30: use IO::File;
                     31: 
1.1       foxr       32: #
                     33: #   Module to read/edit configuration files.
                     34: #   See the POD at the bottom of the file for more information.
                     35: 
                     36: #------------------------------ internal utility functions ----------
                     37: 
                     38: # 
                     39: # Comment 
                     40: #   Returns true if the line is completely a comment.
                     41: # Paramter:
                     42: #    line  
                     43: #        Contents of a configuration file line.
                     44: #
                     45: sub Comment {
                     46:     my $line = shift;
                     47: 
                     48:     # Leading whitespace followed by a #..
                     49: 
                     50:     if ($line =~ /^[' ',\t]*\#/) {
                     51: 	return 1;
                     52:     }
                     53:     # Solely whitespace or empty  line.
                     54: 
                     55:     $line =~ s/[' ',\t]//g;
                     56:     return ($line eq "");
                     57: 
                     58: }
                     59: 
                     60: #
                     61: #  Field
                     62: #    Return the value of a field in the line.  Leading whitespace is trimmed
                     63: #    from the first key (key 0).
                     64: #  Parameters:
                     65: #     line 
                     66: #        Line from which to extract the field.
                     67: #
                     68: #     idx
                     69: #        Index of the field to extract.
                     70: #
                     71: sub Field {
                     72:     my $line = shift;
                     73:     my $idx  = shift;
                     74: 
                     75:     $line =~ s/(^ *)|(^\t*)//;
                     76: 
                     77:     my @fields = split(/:/, $line);
                     78: 
                     79:     return $fields[$idx];
                     80: }
                     81: #
                     82: #   Index:
                     83: #      Return a reference to a hash that indexes a line array.
                     84: #      The hash is keyed on a field in the line array lines
                     85: #      Each hash entry is the line number of the line in which 
                     86: #      that key value appears.  Note that at present, keys must be
                     87: #      unique.
                     88: #  Parameters:
                     89: #      $array    - Reference to a line array.
                     90: #      $idxfield - Field number to index on (0 is the first field).
                     91: #  Returns:
                     92: #    Reference to the index hash:
                     93: sub Index {
                     94:     my $array     = shift;
                     95:     my $idxfield  = shift;
                     96:    
                     97:     my %hash;
                     98:     for(my $l = 0; $l < scalar(@$array); $l++) {
                     99: 	chomp $array->[$l];	# Ensure lines have no \n's.
                    100: 	my $line = $array->[$l];
                    101: 	if(!Comment($line)) {
                    102: 	    my $keyvalue = Field($line, $idxfield);
                    103: 	    $hash{$keyvalue} = $l;
                    104: 	}
                    105:     }
                    106: 
                    107: 
                    108:     return \%hash;
                    109: }
                    110: 
                    111: 
                    112: #------------------------------- public functions --------------------
                    113: #
                    114: #   new
                    115: #     Create a new configuration file editor object.
                    116: #     configuration files are : separated fields that 
                    117: #     may have comments, blank lines and trailing comments.
                    118: #     comments are indicated by #"s.
                    119: #   Parameters:
                    120: #     filename 
                    121: #            Name of file to open.
                    122: #     indexfield
                    123: #            Select the field to index the file by.
                    124: #
                    125: # 
                    126: sub new {
                    127:     my $class      = shift;
                    128:     my $filename   = shift;
                    129:     my $indexfield = shift;
                    130: 
                    131:     # Open the configuration file.  Failure results in the return
                    132:     # of an undef.
                    133:     # Note we dont' need to hold on to the file handle after the file
                    134:     # is read in.
                    135: 
                    136:     open(CONFIGFILE, "< $filename") 
                    137: 	or return undef;
                    138: 
                    139: 
                    140:     #   Read the file into a line array:
                    141: 
                    142:     my @linearray = <CONFIGFILE>;
                    143:     close(CONFIGFILE);
                    144:     
                    145:     
                    146:     #  Build the key to lines hash: this hash
                    147:     #  is keyed on item $indexfield of the line
                    148:     #  and contains the line number of the actual line.
                    149: 
                    150:     my $hashref = Index(\@linearray, $indexfield);
                    151: 
                    152: 
                    153:     #   Build the object hash, bless it and return.
                    154: 
                    155:     my $self       = { Filename   => $filename,
                    156: 		       Indexfield => $indexfield,
                    157: 		       LineArray  => \@linearray,
                    158: 		       KeyToLines => $hashref};
                    159: 
                    160:     bless ($self, $class);
                    161: 
                    162:     return $self;
                    163:     
                    164: }
                    165: #
                    166: #   Append an element to the configuration file array.
                    167: #   The element is placed at the end of the array. If the element is not
                    168: #   a comment. The key is added to the index.
                    169: #
                    170: #   Parameters:
                    171: #      $self     - Reference to our member hash.
                    172: #      $line     - A line to add to the config file.
                    173: sub Append { 
                    174:     my $self    = shift;
                    175:     my $line    = shift;
                    176: 
                    177:     #   Regardless, the line is added to the config file.
                    178: 
                    179:     my $linearray = ($self->{LineArray});
                    180:     push(@$linearray, $line);	                     # Append the line.
                    181:     my $newindex = @$linearray - 1;                  # Index of new line.
                    182: 
                    183:     #   If the line is not a comment, pull out the desired field and add
                    184:     #   it to the line index hash.
                    185: 
                    186:     if(!Comment($line)) {
                    187: 	my $field = Field($line, $self->{Indexfield});
                    188: 	$self->{KeyToLines}->{$field} = $newindex;
                    189:     }
                    190: }
                    191: #
                    192: #   Find a non comment line by looking it up by key.  
                    193: #  Parameters:
                    194: #     $self  - Reference to our member hash.
                    195: #     $key   - Lookup key.
                    196: #  Returns:
                    197: #     Contents of the line or undef if there is no match.
                    198: #
                    199: sub Find {
                    200:     my $self    = shift;
                    201:     my $key     = shift;
                    202: 
                    203:     my $hash    = $self->{KeyToLines};
                    204:     if(defined($hash->{$key})) {
                    205: 	my $lines   = $self->{LineArray};
                    206: 	return $lines->[$hash->{$key}];
                    207:     } else {
                    208: 	return undef;
                    209:     }
                    210: }
                    211: #
                    212: #   Return the number of lines in the current configuration file.
                    213: #   Note that this count includes the comment lines.  To
                    214: #   Get the non comment lines the best thing is to iterate through the
                    215: #   keys of the KeyToLines hash.
                    216: #  Parameters:
                    217: #    $self     - Reference to member data hash for the object.
                    218: #
                    219: sub LineCount {
                    220:     my $self  = shift;
                    221:     my $lines = $self->{LineArray};
                    222:     my $count = @$lines;
                    223:     return $count;
                    224: }
                    225: #
                    226: #   Delete a line from the configuration file.
                    227: #   Note at present, there is no support for deleting comment lines.
                    228: #   The line is deleted, from the array.  All lines following are slid back
                    229: #   one index and the index hash is rebuilt.
                    230: # Parameters:
                    231: #   $self     - Reference to the member data hash for the object.
                    232: #   $key      - key value of the line to delete.
                    233: # NOTE:
                    234: #   If a line matching this key does not exist, this is a no-op.
                    235: #
                    236: sub DeleteLine {
                    237:     my $self     = shift;
                    238:     my $key      = shift;
                    239: 
                    240:     my $lines    = $self->{LineArray};
                    241:     my $index    = $self->{KeyToLines};
                    242:     my $lastidx  = $self->LineCount() - 1;   # Index of last item.
                    243: 
                    244: 
1.2       foxr      245:     my @temp;
1.1       foxr      246: 
                    247:     if(! defined($index->{$key})) {           # bail if no match.
                    248: 	return;
                    249:     }
                    250:     my $itemno   = $index->{$key}; # Index of item to delete.
                    251: 
1.2       foxr      252:     
1.1       foxr      253:     if ($itemno != $lastidx) {               # need to slide and reindex.
1.2       foxr      254: 	@temp = @$lines[0..($itemno-1)];
                    255: 	@temp[$itemno..($lastidx-1)] = @$lines[($itemno+1)..$lastidx];
                    256: 	    
                    257: 
1.1       foxr      258:     } else {			             # just need to truncate
1.2       foxr      259: 	@temp = @$lines[0..$lastidx-1];	     # So take the initial slice.
1.1       foxr      260:     }
1.2       foxr      261: 
                    262:     $self->{KeyToLines} = Index(\@temp, $self->{Indexfield});
                    263:     $self->{LineArray} = \@temp;             # Replace the lines index. 
1.1       foxr      264: 
                    265: 
                    266: }
                    267: #
                    268: #   Replace a line in the configuration file:
                    269: #   The line is looked up by index.
                    270: #   The line is replaced by the one passed in... note if the line
                    271: #   is a comment, the index is just deleted!!
                    272: #   The index for the line is replaced with the new value of the key field
                    273: #  (it's possible the key field changed).
                    274: # 
                    275: #  Parameters:
                    276: #     $self          - Reference to the object's member data hash.
                    277: #     $key           - Lookup key.
                    278: #     $line          - New line.
                    279: # NOTE:
                    280: #  If there is no line with the key $key, this reduces to an append.
                    281: #
                    282: sub ReplaceLine {
                    283:     my $self       = shift;
                    284:     my $key        = shift;
                    285:     my $line       = shift;
                    286: 
                    287:     my $hashref  = $self->{KeyToLines};
                    288:     if(!defined $hashref->{$key}) {
                    289: 	$self->Append($line); 
                    290:     } else {
                    291: 	my $l     = $hashref->{$key};
                    292: 	my $lines = $self->{LineArray};
                    293: 	$lines->[$l] = $line;	          # Replace old line.
                    294: 	delete $hashref->{$key};          # get rid of the old index.
                    295: 	if(!Comment($line)) {	          # Index this line only if not comment!
                    296: 	    my $newkey = Field($line, $self->{Indexfield});
                    297: 	    $hashref->{$newkey} = $l;
                    298: 	}
                    299:     }
                    300: }
1.2       foxr      301: #
                    302: #   Write the configuration array to a file:
                    303: #   Parameters:
                    304: #      $self           - Reference to the object's member data hash.
                    305: #      $fh              - Name of file to write.
                    306: sub Write {
                    307:     my $self     = shift;
                    308:     my $fh       = shift;
                    309:     
                    310:     my $lines    = $self->{LineArray};
                    311:     my $length   = @$lines;
                    312:     for (my $i = 0; $i < $length; $i++) {
                    313: 	print $fh $lines->[$i]."\n";
                    314:     }   
                    315: }
                    316: 
1.3       foxr      317: #   Get:
                    318: #      return the entire contents of the file as a string.
                    319: # Parameters:
                    320: #    $self      - (this).
                    321: #
                    322: sub Get {
                    323:     my $self    = shift;
                    324:     
                    325:     my $contents = "";
                    326:     my $lines    = $self->{LineArray};
                    327:     my $length   = @$lines;
                    328: 
                    329:     for (my $i = 0; $i < $length; $i++) {
                    330: 	$contents .= $lines->[$i]."\n";
                    331:     }
                    332:     return $contents;
                    333: }
                    334: 
1.1       foxr      335: 1;
1.2       foxr      336: #----------------------------- Documentation --------------------------------------
                    337: #
                    338: 
                    339: =pod
                    340: 
                    341: =head1 NAME
                    342: 
                    343: ConfigFileEdit - Lookups and edits on a configuration file.
                    344: 
                    345: =head1 SYNOPSIS
                    346: 
                    347:     use LONCAPA::ConfigFileEdit;
                    348:     use IO::File;
                    349: 
                    350:     my $editor = ConfigFileEdit->new("file.cfg", 0);
                    351:     $editor->Append("new:line:with:config:info");      
                    352:     my $line   = $editor->Find("key");
                    353:     my $size   = $editor->LineCount();
                    354:     $editor->DeleteLine("george");
                    355:     $editor->ReplaceLine("new","new:line:with:different:info");  
                    356:     my $fh = new IO::File("> modified.cfg", 0);
                    357:     $editor->Write($fh);
                    358: 
                    359: =head1 DESCRIPTION
                    360: 
                    361: Configuration files in LonCAPA contain lines of colon separated fields.
                    362: Configuration files can also contain comments initiated by the hash (#)
                    363: character that continue to the end of line.  Finally, configuration files
                    364: can be made more readable by the inclusion of either empty lines or 
                    365: lines entirely made up of whitespace.
                    366: 
                    367: ConfigFileEdit allows manipulation of configuration files in a way that
                    368: preserves comments and order.  This differs from LonCAPA's 'normal' mechanism
                    369: handling configuration files by throwing them up into a hash by key.
                    370: 
                    371: ConfigFileEdit maintains the original configuration file in an array and
                    372: creates the index hash as a hash to line numbers in the array.  This allows
                    373: O(k) time lookups, while preserving file order and comments (comments are
                    374: lines in the array that have no indices in the associated hash.
                    375: 
                    376: In addition to line lookup, ConfigFileEdit supports simple editing
                    377: functions such as delete, append, and replace.  At present, Insertions
                    378: at arbitrary points in the file are not supported.  The modified
                    379: file can also be written out.
                    380: 
                    381: =head1 METHODS
                    382: 
                    383: =head2 new ( filename, keyfield )
                    384: 
                    385: Creates  a new ConfigFileEdit object from an existing file.   Where:
                    386: 
                    387: =over 4
                    388: 
                    389: =item * filename - The name of the configuration file to open.
                    390: 
                    391: =item * keyfield - The number of the field for which the index hash is generated.
                    392:     Fields are enumerated from zero.
                    393: 
                    394: =item * RETURNS: - undef if the file could not be open, otherwise a reference
                    395:     to a hash that contains the object member data.
                    396: 
                    397: =back
                    398: 
                    399: =head2 Append ( newline )
                    400: 
                    401: Appends a new line to the configuration file in memory.  The file that was
                    402: used to create the object is not modified by this operation.
                    403: 
                    404: =over 4
                    405: 
                    406: =item * newline - A new line to append to the end of the configurationfile.
                    407: 
                    408: =back
                    409: 
                    410: =head2 LineCount 
                    411: 
                    412: Returns the number of lines in the file.  This count includes the nubmer of
                    413: comments.
                    414: 
                    415: =head2 DeleteLine ( key )
                    416: 
                    417: Deletes the line that matches key.  Note that if there is no matching line,
                    418: this function is a no-op.
                    419: 
                    420: =over 4
                    421: 
                    422: =item * key   - The key to match, the line that is indexed by this key is deleted.
                    423: 
                    424: =back
                    425: 
                    426: =head2 ReplaceLine ( key, newcontents  )
                    427: 
                    428: Replaces the selected line in its entirety.  Note that the config file is re-indexed
                    429: so it is legal to modify the line's key field.
                    430: 
                    431: =over 4
                    432: 
                    433: =item * key    - The key that selects which line is replaced.
                    434: 
                    435: =item * newcontents - The new contents of the line.
                    436: 
                    437: =back
                    438: 
                    439: =head2 Write ( fh )
                    440: 
                    441: Writes the contents of the configuration file's line array to file.
                    442: 
                    443: =over 4
                    444: 
                    445: =item * fh   - A file handle that is open for write on the destination file.
                    446: 
                    447: =back
                    448: 
1.3       foxr      449: =head2 Get ()
                    450: 
                    451: Return the entire contents of the configuration file as a single string.
                    452: 
1.2       foxr      453: =head2 Comment ( line )
                    454: 
                    455: Static member that returns true if the line passed in is a comment or blank line.
                    456: 
                    457: =head2 Field ( line, index )
                    458: 
                    459: Static member that returns the value of a particular field on a config file line.
                    460: 
                    461: =over 4
                    462: 
                    463: =item * line   - The line that's parsed.
                    464: 
                    465: =item * index  - The index requested (0 is the first field on the line).
                    466: 
                    467: =back
1.3       foxr      468: 
                    469: 
1.2       foxr      470: 
                    471: =head2 Index ( linearray, fieldno )
                    472: 
                    473: Returns a reference to a hash that indexes a line array by a particular field number.
                    474: this can be used to produce secondary indices if required by the application (see
                    475: MEMBER DATA below).
                    476: 
                    477: =over 4
                    478: 
                    479: =item * linearray - A reference to an array containing text in configuration file format.
                    480: =item * fieldno - Number of the field to index (0 is the first field).
                    481: 
                    482: =item * RETURNS - A reference to a hash from field value to line array element number.
                    483: 
                    484: =back
                    485: 
                    486: =head1 MEMBER DATA
                    487: 
                    488: The following member data can be considered exported.
                    489: 
                    490: =head2 LineArray
                    491: 
                    492: The reference to the configuration files line array.
                    493: 
                    494: =cut
                    495: 
1.1       foxr      496: 
1.2       foxr      497: __END__

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.