Cover V05, I08
Article
Figure 1
Figure 2
Figure 3
Listing 1
Listing 2
Sidebar 1

aug96.tar


Listing 2: AdminLogDB

#!/usr/local/bin/wish -f

# -----------------------------------------------------
# AdminLogDB                         September 28, 1995
# Scott A. Tarvainen
# -----------------------------------------------------
# This TCL/TK wish program is a modification of the
# tkdb program written by Norman Walsh.  The AdminLogDB
# program is a graphical System Administrator Log
# program to allow system administration staff members
# to share information of technical/historical
# importance on a daily basis.
# Note:  The tkdb code is reprinted with Mr. Walsh's
#        consent.
# -----------------------------------------------------

#
# tkdb is a simple "database" editor.  A database is a collection of
# records with text fields.
#
# Copyright (C) 1994 by Norman Walsh
#
# In order to use tkdb, you must first create the database file headers
# with another editor.  Database files have the following format: line 1
# holds the database title, line 2 holds the field specification, and lines
# 3 through eof each hold a single record.
#
# The database field specification consists of an arbitrary number of fields.
# Each field consists of a fieldname and a length.  For example, "Name 30"
# identifies a field called "Name" that is 30 characters long.  Field names
# must not have imbeded commas (,) or semicolons (;).
#
# Fields that are seperated by commas are presented horizontally on the same
# line.  Fields that are seperated by semicolons are presented on seperate
# lines.
#
# Here's an example of a simple database of names and addresses
#
# Names and Addresses
# Name 30;Addr1 30;Addr2 30;City 25,State 2,Zip 10
#
# When displayed, it looks something like this:
#
# +----------------------------------------+
# |         Names and Addresses            |
# |                                        |
# | Name __________________________        |
# | Addr1 __________________________       |
# | Addr2 __________________________       |
# | City ____________ State __ Zip _______ |
# |                                        |
# +----------------------------------------+
#
#########################################################################

# Get the dbName from the arguments, use ~/.tkdbfile if
# none is provided
set arglist [ split $argv " " ]
if { [ llength $arglist ] > 0 } {
set dbName [ lindex $arglist 0 ]
} else {
set dbName "~/.tkdbfile"
}

# Make sure the database exists
if [ file exists "$dbName" ] {
set f [ open "$dbName" ]
} else {
puts stderr "The database file $dbName does not exist."
exit 1
}

# Read the database title and pack it into the frame
gets $f DatabaseTitle
label .title  -text "$DatabaseTitle" \
-font "-*-times-bold-r-*-*-*-180-*" \
-relief ridge

pack  .title -side top -padx 3m -pady 3m

# Load the field specification
if { [ gets $f FieldSpec ] < 0 } {
puts stderr "Cannot read field specification from $dbName."
exit 1
}

# In this context, linecount is lines in the window...
set linecount 0

set FieldCount 0

# Find the widest first field on the line
set ffWidth 0
# Parse the field specification
foreach line [ split $FieldSpec ";" ] {
set field [ lindex [ split $line "," ] 0 ]
if { [ regexp {(.*) ([0-9]+)} "$field" \
fmatch fname flength ] } {
if { [ string length $fname ] > $ffWidth } {
set ffWidth [ string length $fname ]
}
}
}

# Parse the field specification
foreach line [ split $FieldSpec ";" ] {
set linecount [incr linecount]
eval frame .line_$linecount

foreach field [ split $line "," ] {
set FieldCount [incr FieldCount]

if { [ regexp {(.*) ([0-9]+)} "$field" \
fmatch fname flength ] } {
eval label .lf$FieldCount \
-text {$fname} \
-width $ffWidth \
-anchor w
eval entry .ef$FieldCount -relief sunken \
-textvariable Fields($FieldCount) \
-width $flength

eval pack  .lf$FieldCount \
-side left -padx 1m \
-in .line_$linecount
eval pack  .ef$FieldCount \
-side left \
-padx 1m \
-in .line_$linecount
} else {
puts stderr "Error: bad field: $field";
exit 1;
}
}

eval pack .line_$linecount -side top -pady .5m -fill x
}

# Add the buttons and the "Record n of m" line to the
# bottom of the window

#------------------------------------------------------------#
# Print and Delete buttons added by Scott Tarvainen, CTI     #
# to be used in the System Administrator Log Program         #
#------------------------------------------------------------#

frame .crframe
frame .buttons
button .save -text "Save" -command "save"
button .quit -text "Quit" -command "quit"
button .print -text "Print" -command "print"
button .delete -text "Delete" -command "delete"
button .next -text "Next" -command "next 1"
button .prev -text "Prev" -command "next -1"
button .new  -text "New"  -command "new_rec"
pack .save .quit .print .delete .next .prev .new \
-in .buttons \
-side left -padx 1m
pack .buttons \
-side left \
-pady 1m \
-padx 5m \
-in .crframe

set RecNum 1
label .cr_record  -text "Record "
label .cr_current -textvariable RecNum
label .cr_of      -text " of "
label .cr_count   -textvariable RecordCount
pack  .cr_record .cr_current .cr_of .cr_count \
-side left \
-in .crframe

pack .crframe -side top

# We begin at record 1, so disable the "prev" button
.prev configure -state disabled

# Load all the records
set RecordCount 0
while { [ gets $f line ] >= 0 } {
set RecordCount [incr RecordCount]
set Records($RecordCount) "$line"
}

# If there's only one record, disable the "next" button
if { $RecordCount < 2 } {
.next configure -state disabled
}

# we don't need the database file anymore...
close $f

#------------------------------------------------------------#
# set input focus and keystroke bindings to allow carriage   #
# returns                                                    #
#------------------------------------------------------------#

focus .ef1

bind .ef1 <Return> {focus .ef2}
bind .ef2 <Return> {focus .ef3}
bind .ef3 <Return> {focus .ef4}
bind .ef4 <Return> {focus .ef5}
bind .ef5 <Return> {focus .ef6}
bind .ef6 <Return> {focus .ef7}
bind .ef7 <Return> {focus .ef8}
bind .ef8 <Return> {focus .ef9}
bind .ef9 <Return> {focus .ef10}
bind .ef10 <Return> {focus .ef11}
bind .ef11 <Return> {focus .ef12}
bind .ef12 <Return> {focus .ef1}

proc get_rec { } {
# Copy the contents of the current record into
# the Fields()
global Records RecordCount RecNum
global Fields FieldCount

for { set fieldcount 1 } \
{ $fieldcount <= $FieldCount } \
{ incr fieldcount } {
set Fields($fieldcount) ""
}

set fieldcount 0
set line $Records($RecNum)

foreach field [ split $line "|" ] {
set fieldcount [incr fieldcount]
eval set Fields($fieldcount) { $field }

if { $fieldcount == $FieldCount } break
}
}

proc set_rec { } {
# Copy the contents of the Fields() into
# the current record
global Records RecordCount RecNum
global Fields FieldCount
global Changed

set fieldcount 0
set line ""

for { set fieldcount 1 } \
{ $fieldcount <= $FieldCount } \
{ incr fieldcount } {
if { "$line" != "" } {
set line "$line|"
}
set line "$line$Fields($fieldcount)"
}

# Check to see if the record has been modified
if { "$Records($RecNum)" != "$line" } {
set Changed 1
}

set Records($RecNum) "$line"
}

proc save { } {
# Save the database
global DatabaseTitle FieldSpec
global Records RecordCount
global Changed
global dbName

set_rec

set f [ open "$dbName" "w" ]

puts $f "$DatabaseTitle"
puts $f "$FieldSpec"

for { set i 1 } { $i <= $RecordCount } { incr i } {
puts $f "$Records($i)"
}

close $f
set Changed 0
}

proc quit { } {
# Quit, making sure we don't accidentally lose
# any changes
global Changed

set_rec

set really_quit 1
if { $Changed } {
set really_quit [ confirm_quit ]
}

if { $really_quit } {
destroy .
exit 0
}
}

proc confirm_quit {} {
# Do you *really* want to lose those changes?
set reply [ tk_dialog .d "Are you sure?" \
"There are unsaved changes that will be lost, \
are you sure you want to quit?" \
warning 0 "Save" "Cancel" "Discard changes" ]

if { $reply == 0 } {
save
return 1
}

if { $reply == 2 } {
return 1
}

return 0
}

#------------------------------------------------------------#
# Procedure print_records added by Scott Tarvainen, CTI to   #
# create an ASCII output file that can be queued to the      #
# printer.  This ASCII file will be created and named        #
# "/tmp/printfile".  The last line of this procedure is an   #
# exec call to lpr to print to your printer.  This assumes   #
# the default printer 'lp' is set in your /etc/printcap file.#
#------------------------------------------------------------#

proc print_records {} {

global dbName
global Records RecordCount RecNum

set PrintFile "/tmp/printfile"
set f [ open "$PrintFile" "w" ]

if [ file exists "$dbName" ] {
set e [ open "$dbName" "r" ]
} else {
puts stderr "The database file $dbName does not exist."
exit 1
}

set count 0
while { [ gets $e line ] >= 0 } {
set count [ incr count ]
if { $count == 1 } {
puts $f "               ============================================"
puts $f "              $line"
puts $f "               ============================================"
puts $f ""
}

if { $count > 2 } {
set fieldcount 0
foreach field [ split $line "|" ] {
set fieldcount [ incr fieldcount ]
set MYFields($fieldcount) "$field"
}
puts $f "               Time           Administrator"
puts $f "               ----           -------------"
puts $f "               $MYFields(2)          $MYFields(1)"
puts $f ""

for { set j 3 } { $j <= $fieldcount } { incr j } {
if { $MYFields($j) != "" } {
puts $f "               $MYFields($j)"
}
}
puts $f ""
puts $f "               ############################################"
puts $f ""
}
}
close $e
close $f
exec lpr /tmp/printfile
}

#------------------------------------------------------------#
# Procedure print added by Scott Tarvainen, CTI to allow the #
# user the option to Save and Print the Sysadmin Log, Ignore #
# Changes and Print, or to Cancel.                           #
#------------------------------------------------------------#

proc print {} {

global DatabaseTitle FieldSpec dbName
global Records RecordCount RecNum
global Fields FieldCount Changed

set_rec

if { $Changed } {
set reply [ tk_dialog .d "Print Option" \
"There are unsaved changes \
to the database" \
warning 0 "Save Changes & Print" \
"Ignore Changes & Print" "Cancel" ]

if { $reply == 0 } {
save
print_records

} elseif { $reply == 1 } {
print_records

} else {
}
} else {
print_records
}
}

#------------------------------------------------------------#
# Procedure delete added by Scott Tarvainen, CTI to give the #
# user the option to delete a record from the Sysadmin Log.  #
#------------------------------------------------------------#

proc delete {} {

global DatabaseTitle FieldSpec dbName
global Records RecordCount RecNum
global Fields FieldCount Changed

if { $RecordCount < 2 } {
for { set fieldcount 1 } \
{ $fieldcount <= $FieldCount } \
{ incr fieldcount } {
set Fields($fieldcount) ""
}
.next configure -state disabledd
set Changed 1

} else {
if { $RecNum == $RecordCount } {
set RecNum [ incr RecNum -1 ]
set RecordCount [ incr RecordCount -1 ]
get_rec
.next configure -state disabled
set Changed 1

} else {
set MinusCount [ incr RecordCount -1 ]
for { set i $RecNum } { $i <= $MinusCount } \
{incr i 1 } {
set j $i
set NextRecord [ incr j 1 ]
set Records($i) "$Records($NextRecord)"
}

if { $RecNum > 1 } {
set RecNum [ incr RecNum -1 ]
get_rec
} else {
set RecNum 1
get_rec
.prev configure -state disabled
}
set Changed 1
}
}

if { $RecNum == $RecordCount } {
.next configure -state disabled
}
if { $RecNum < 2 } {
.prev configure -state disabled
}
}

proc next direction {
# Move forward or backward, updating the buttons
global RecNum RecordCount

set_rec

set nextrec [ incr RecNum $direction ]

if { $nextrec == 1 } {
.prev configure -state disabled
} else {
.prev configure -state active
}

if { $nextrec == $RecordCount } {
.next configure -state disabled
} else {
.next configure -state active
}

set RecNum $nextrec

get_rec
}

proc new_rec {} {
# Create a new record
global RecNum RecordCount Records
global Changed

if { $RecordCount > 0 } {
set_rec
}

set RecordCount [ incr RecordCount ]
set RecNum $RecordCount

set Records($RecNum) ""

get_rec

.prev configure -state disabled
.next configure -state disabled

if { $RecNum > 1 } {
.prev configure -state active
}

set Changed 1
}

# If there are no records, create one
if { $RecordCount == 0 } {
new_rec
}

set Changed 0

get_rec

# "wish" will take over here and begin processing
# button and entry events..

# End of File