#!/bin/sh
#
# Contribution to "Sprachwahl" from Linux Magazin 2008/10
#
# Solution for script language TCL
#
#    (c) Gerhard Reithofer,
#    Techn. EDV Reithofer; 2008-09-07
#
# Usage: Usage: tclsh reorder [sortopt] [memopt] infile [outfile]
#
#  sortopt: none .. footnotes are rebuild in order as they appear in
#                   footnote list (1..n)
#           -t .... footnotes are renumbered as they appear in text,
#                   order of footnotes in list is preserved
#           -f .... footnotes are renumbered as they appear in text,
#                   footnotes list is reordered also
# memopt:   none .. file is preprocessed line by line
#           -m .... complete file is read into memory at begin,
#                   no check for existing memory is done
# infile: ......... name of input file, not checked (exit, readable...)
# outfile: ........ name of output file, will be overwritten without
#                   confirmation, default is stdout
#
#
# starting tclsh \
exec tclsh $0 "$@"

### switch between file mode and memory mode
set file_mode_solution 1

### footnote begin marker
set fn(mark) "@footnote:"

### footnote counter
set fn(cntr) 0

### default - do not sort footnotes in text
set fn(sort) 0

### default - do not (re)sort footnotes
set fn(note) 0

### footnote data
array set fn_data {}

### default standard output
set ofd stdout

###
proc syntax {args} {
 if {[llength $args]} { puts [lindex $args 0] }
 puts "Usage: [file tail $::argv0] \[-t|-f\] \[-m\] infile \[outfile\]"
 puts " -t .. sort footnotes in text (default: sort footnotes at end only)"
 puts " -f .. sort footnotes in text and reorder footnotes at end"
 puts " -m .. read complete file to memory (default: line by line file I/O)"
 exit 1
}

### simple add & count note function
proc add_note id {
 global fn_data fn
 if {![info exists fn_data($id)]} {
   set fn_data($id) [incr fn(cntr)]
 }
 return $fn_data($id)
}

###
### process text and reorder footnotes
### in text if ($reorder == 1)
###
proc process_text {fd ofd reorder} {
 global fn fn_data
 set cnt 0
 next $fd line
 while {$line ne $fn(mark)} {
   set rres [regexp -indices {\[([0-9]+)\]} $line pos num]

   if {!$rres} {
     puts $ofd $line
     next $fd line
     continue
   }

### process all footnotes in $line
###
   set eidx 0
   set rlin ""
   while {$rres} {
     set id [string range $line [lindex $num 0] [lindex $num 1]]
     set s1 [string range $line $eidx [lindex $pos 0]]
     append rlin $s1
     if {$reorder} {
       append rlin $fn_data($id)
     } else {
       set new [add_note $id]
       append rlin $new
     }
     set eidx [lindex $pos 1]
     set rres [regexp -indices -start $eidx {\[([0-9]+)\]} $line pos num]
   }

   set s2 [string range $line [lindex $pos 1] end]
   append rlin $s2

### write out corrected line and read next...
###
   puts $ofd $rlin
   next $fd line
 }
}

###
### process footnote section
###
proc write_notes {fd ofd reorder} {
 global fn fn_data

 puts $fn(mark)

 next $fd line
 while {![enddata $fd]} {
   if {$line eq ""} {
     if {![info exists data]} {puts $ofd ""}
     next $fd line
     continue
   }
   if {[string first {[} $line]>=0} {
     set lc [string first {]} $line]
     set id [string range $line 1 [incr lc -1]]
     set line [string range $line [incr lc 2] end]
     lappend data [list $fn_data($id) $line]
   }
   next $fd line
 }

 if {$reorder} {
   set data [lsort -index 0 -integer $data]
 }

 foreach r $data {
   set id [lindex $r 0]
   puts $ofd "\[$id\][lindex $r 1]"
 }
}

###
### read footnote section 1st, for
### determination of footnote order
proc preproc_notes fd {
 global fn fn_data

 # read over all text lines...
 set line ""
 while {$line ne $fn(mark)} {
   next $fd line
 }

 while {![enddata $fd]} {
   next $fd line
   if {[string index $line 0] ne {[}} continue
   set lc [string first {]} $line]
   set id [string range $line 1 [incr lc -1]]
   add_note $id
 }

 # rewind file
 reset $fd
}

###
### commandline handling and opening of
### files - no further file checks are made!!!
###
if {$argc<1} { syntax }
foreach p $argv {
 if {[string index $p 0] eq "-"} {
   switch -- $p {
     "-t" {set fn(sort) 1}
     "-f" {set fn(sort) 1;set fn(note) 1}
     "-m" {set file_mode_solution 0}
     default {syntax "Invalid option: '$p'"}
   }
 } else {
   if {[info exists ifd]} {
     set ofd [open $p w+]
   } else {
     set ifd [open $p r]
   }
 }
}

##########################################################
### begin of dummy file functions
###
if {$file_mode_solution} {
 proc next {dv rv} { upvar 1 $rv line ; gets $dv line }
 proc enddata {dv} { return [eof $dv] }
 proc reset {dv}   { seek $dv 0 }
} else {
 array set lines {cnt -1 max 0 mem {}}
 proc next {dv rv} {
   global lines ; upvar 1 $rv line
   set line [lindex $lines(mem) [incr lines(cnt)]]
 }
 proc enddata {dv} {
   global lines
   return [expr {$lines(cnt)>=$lines(max)}]
 }
 proc reset {dv} { global lines ; set lines(cnt) -1}
}
###
### end of dummy file functions
##########################################################


if {![info exists ifd]} {
 syntax "Syntax error: no inputfile given!"
}

###
### M A I N part starts here
###
if {!$file_mode_solution} {
 set lines(mem) [split [read -nonewline $ifd] "\n"]
 set lines(max) [llength $lines(mem)]
}

if {!$fn(sort)} {
 preproc_notes $ifd
 process_text $ifd $ofd 1
} else {
 process_text $ifd $ofd 0
}

write_notes  $ifd $ofd $fn(note)

close $ifd
close $ofd