#!/c/bin/perl

# Henna Pietiläinen, filedoc -- Tiedostodokumentaatiotietokanta, 
# Tik-76.004 Ohjelmointi II
#
# Kyseessä on tiedostojen dokumentointiin soveltuva ohjelma, jolla voi ylläpitää 
# tiedostoihin liittyviä lyhyitä kommentteja. Ohjelma kysyy kommentit uusille 
# tiedostoille ja siinä on ls, rm, cp ja mv komentoja vastaavat toiminnot cls, 
# crm, ccp ja cmv, jotka siirtävät myös tiedoston dokumentoinnin samalla. Komentojen 
# optioista on toteutettu -i, (muille paitsi cls:lle jolle se on tarpeeton).  
#
# Kommentit on mahdollistettu pitämällä kussakin hakemistossa .comments -nimistä 
# tiedostoa. Etuina hakemistokohtaisessa tiedostossa käyttäjäkohtaiseen globaaliin 
# tietokantaan on pienempi koko, nopeampi haku ja lukitukset sulkevat vain yhden 
# hakemiston kommentit. Haittana on se että se ei ole käyttäjäkohtainen.

use Fcntl ':flock'; # import LOCK_* constants

$USAGE_CLS = "cls\n";
$USAGE_CRM = "crm [-i] file ...\n -i asks before removing\n";
$USAGE_CCP = "ccp [-i] SOURCE DEST\n -i asks before copying\n";
$USAGE_CMV = "cmv [-i] SOURCE DEST\n -i asks before moving\n";

# Aliohjelma tiedoston ja kuvauksen kirjoittamiseksi tiedostoon:
sub addToTable {
    $fileName = $_[0];
    $tiedosto = $_[1];
    $kuvaus = $_[2];
    
    open (TAULU, "+>> $fileName");  # Avataan tiedosto tiedoston ja kuvauksen 
                                    # lisäämistä varten.
    flock(TAULU, LOCK_EX);          # Lukitaan tiedosto.
    
    while ($temp = <TAULU>) {
	chop($temp);
	($tied, $kuv) = split(/\//, $temp);
	if ($tiedosto eq $tied) {   # Jos tiedostossa on jo lisättävän tiedoston 
	                            # niminen tiedosto lopetetaan lisäys.
	    flock(TAULU, LOCK_UN);  # Avataan lukko.
	    close(TAULU);           # Suljetaan taulu.
	    return;
	}
    }
    seek(TAULU, 0, 2);              # Kelataan tiedoston loppuun.
    print TAULU $tiedosto."/".$kuvaus."\n";  # Tulostetaan näytölle.
    flock(TAULU, LOCK_UN);          # Lukitaan tiedosto.
    close(TAULU);                   # Suljetaan tiedosto.
    return;
}

# Aliohjelma tiedoston ja kuvauksen poistamiseksi tiedostosta:
sub removeFromTable {
    $fileName = $_[0];
    $tiedosto = $_[1];
    $kuvaus = $_[2];

    open (TAULU, $fileName);        #Avataan tiedosto1 lukemista varten.
    open (VARATAULU, ">.temp");     #Avataan tiedosto2 tiedostojen ja kuvauksien
                                    #kirjoittamista varten.
    flock(TAULU, LOCK_EX);          #Lukitaan tiedosto1.
    flock(VARATAULU, LOCK_EX);      #Lukitaan tiedosto2.

    while ($temp = <TAULU>) {
	chop($temp);
	($tied, $kuv) = split(/\//, $temp);
	
	if ($tied ne $tiedosto) {      #Jollei ole poistettava tiedosto, kirjoitetaan 
	                               #tiedosto2:een.
	    print VARATAULU "$temp\n"; 
	}
    }
    rename(".temp", "$fileName");      #Korvataan tiedosto1 tiedosto2:lla.
    flock(TAULU, LOCK_UN);             #Avataan lukko.
    flock(VARATAULU, LOCK_UN);         #Avataan lukko.
    close(TAULU);                      #Suljetaan tiedosto1.
    close(VARATAULU);                  #Suljetaan tiedosto2.
}

# Aliohjelma jolla määritetään halutun tiedoston polku ja nimi:
sub whichDirectory {
    $numberOfArgument = $_[0];
   
    if ($_[1] eq "-i") {
       $number = $numberOfArgument + 1;
   }
    else {
	$number = $numberOfArgument; }
    
    if (-d $_[$number]){                      #Jos on kyseessä hakemisto on
	$file[0] = $_[$number].".comments";   #tiedoston polku annettu polku +
	$file[1] = `basename $_[$number - 1]`;#.comments ja nimi kopioitavan tai
                                              #siirrettävän tiedoston nimi.
    }
    else {
	$file[0] = $_[$number];                
	$file_length = length($file[0]);       #Muuten testataan onko tiedosto 
	                                       #tässä hakemistossa vai jossain
                                               #muualla.
	for ($i = 0; $i < $file_length && chop($file[0]) ne '/'; $i++) {};
	if ($i == $file_length) {              #Polkua ei ole annettu, joten
	    $file[0] = ".comments";            #tiedosto on tässä hakemistossa,
	    $file[1] = $_[$number];            #polku on .comments ja nimi annettu nimi.
	} else {                               
	    $file[0] = $file[0]."/.comments";  #On annettu polku. Nyt lisätään polun 
	    $file[1] = `basename $_[$number]`; #viimeisen /-merkin jälkeen .comments,
                                               #tiedoston nimeksi tulee annettu
	                                       #nimi.
	}
    }
    return @file;
}

# cls -käsky:
if ($0 =~ /cls$/ ) {

    if ($#ARGV >= 0) {                        #Jos cls:lle annetaan argumentteja
	print $USAGE_CLS;                     #tulostuu käyttö ja ohjelma lopetetaan. 
	exit;} 
    else {
	open (TAULUKKO, "<.comments");         #Avataan .comments lukemista varten 
	while ($temp = <TAULUKKO>) {        
	    chop($temp);
	    ($tiedosto, $kuvaus) = split(/\//, $temp, 2);
	    print "$tiedosto:   $kuvaus\n";     #Tulostetaan tiedoston sisältö
	}                                       #näytölle.
	close (TAULUKKO);                       #Suljetaan .comments.
    }
}	    


# crm -käsky:
if ($0 =~ /crm$/ ) {
    my $i;
    $maara = $#ARGV + 1;                        #Argumenttien määrä.

    if ($ARGV[0] eq "-i") {
	$number = 1;}
    else { 
	$number = 0;  }

    for ($i = $number; $i < $maara; $i++) {     #Käydään kaikki argumentit läpi,
	                                        #paitsi -i.
	if (-e $ARGV[$i]){}                     #Jollei poistettavaa tiedostoa
	else {                                  #löydy annetaan virheilmoitus.
	    print "crm: $ARGV[$i]: No such file\n"; } 
	    
	@hakemisto = whichDirectory(1,@ARGV[$i]);#Määritetään tiedoston polku
	open (TAULUKKO, $hakemisto[0]);          #Avataan tiedostolle oikea
                                                 #.comments -tiedosto.
	while ($temp = <TAULUKKO>) {             #Luetaan .comments -tiedostoa.

	    ($tiedosto, $kuvaus) = split(/\//, $temp, 2);
	    if ($hakemisto[1] eq $tiedosto) {    #Tutkitaan löytyykö .commentsista 
		                                 #poistettava tiedosto.
		if ($ARGV[0] eq "-i") {
		    print "File $ARGV[$i]. Remove ? (y/n): ";  #Jos -i -optio var-
		    $vastaus = <STDIN>;                        #mistetaan poisto.
		    chop($vastaus);
		
		if ($vastaus eq "y") {            #Jos vastaus on y poistetaan.
		    removeFromTable($hakemisto[0], $hakemisto[1], $kuvaus);
		    system("rm", $ARGV[$i]);
		}
	    }                                     #Jollei -i -optiota niin  
		else {                            #poistetaan kysymättä.
		    removeFromTable($hakemisto[0], $hakemisto[1], $kuvaus);
		    system("rm", $ARGV[$i]);
		}  
	    }
	}
	close(TAULUKKO);                          #Suljetaan .comments.
    }
}

# ccp -käsky:
if ($0 =~ /ccp$/ ) {
 
    $maara = $#ARGV + 1;                        #Argumenttien määrä.
    @hakemisto1 = whichDirectory(1,@ARGV);      #Ensimmäisen argumentin polku.
    @hakemisto2 = whichDirectory(2,@ARGV);      #Toisen argumentin polku.
    open (TAULUKKO, $hakemisto1[0]);            #Avataan oikea .comments 
                                                #ensimmäiselle argumentille. 
    if ($ARGV[0] eq "-i") {                     
	$number = 1;}
    else {
	$number = 0; }
    if (($maara > $number + 2) or ($maara < $number - 2)) { #Jos argumentteja ei
	print $USAGE_CCP;                                   #ole kahta annetaan
	exit; }                                             #virheilmoitus.
    if (-e $ARGV[$number]) {}                         #Jollei kopioitavaa tiedostoa
    else {                                            #ole olemassa annetaan
	print "ccp: $ARGV[$number]: No such file\n"; }#virheilmoitus.
    while ($temp = <TAULUKKO>) {                          #Luetaan .commentsia.
	chop($temp);
	($tiedosto, $kuvaus) = split(/\//, $temp, 2);
	if ($ARGV[0] eq "-i") {                       #Jos -i -optio varmistetaan
	                                              #käyttäjältä kopiointi.
	    if ($hakemisto1[1] eq $tiedosto) {
		print ("Copy file $ARGV[1] to file $ARGV[2] ? (y/n): ");
		$vastaus = <STDIN>;
		chop($vastaus);
		if ($vastaus eq "y") {                #Jos vastaus on y kopioidaan.
		    addToTable($hakemisto2[0], $hakemisto2[1], $kuvaus);
		    system("cp", $ARGV[1], $ARGV[2]);
		}
	    }
	}
	else {                                         #Jollei -i -optiota ole niin
	                                               #kopioidaan kysymättä.
	    if ($hakemisto1[1] eq $tiedosto) {
		addToTable($hakemisto2[0], $hakemisto2[1], $kuvaus);
		system("cp", $ARGV[0], $ARGV[1]);
	    }	    
	}
    }
    close(TAULUKKO);                                    #Suljetaan .comments.
}

# cmv -käsky:
if ($0 =~ /cmv$/ ) {

    $maara = $#ARGV + 1;                            #Argumenttien määrä.
    @hakemisto1 = whichDirectory(1,@ARGV);          #Ensimmäisen argumentin polku.
    @hakemisto2 = whichDirectory(2,@ARGV);          #Toisen argumentin polku.
    open (TAULUKKO, $hakemisto1[0]);                #Avataan ensimmäiselle
                                                    #argumentille oikea .comments.
    if ($ARGV[0] eq "-i") {
	$number = 1;}
    else {
	$number = 0; }
    if (($maara > $number + 2) or ($maara < $number - 2)) { #Jollei argumentteja ole 
	print $USAGE_CMV;                                   #kaksi, annetaan
	exit; }                                             #virheilmoitus.
    if (-e $ARGV[$number]) {}                               #Jollei siirrettävää
    else {                                                  #tiedostoa ole olemassa
	print "cmv: $ARGV[$number]: No such file\n"; }      #annetaan virheilmoitus.

    while ($temp = <TAULUKKO>) {                            #Luetaan .commentsia.
	chop($temp);
	($tiedosto, $kuvaus) = split(/\//, $temp, 2);
	if ($ARGV[0] eq "-i") {                       #Jos on -i -optio kysytään vielä
	                                              #ennen siirtoa käyttäjältä.
	    if ($hakemisto1[1] eq $tiedosto) {
		print ("Move file $ARGV[1] to file $ARGV[2] ? (y/n): ");
		$vastaus = <STDIN>;
		chop($vastaus);
		if ($vastaus eq "y") {                 #Jos vastaus on y siirretään.
		    addToTable($hakemisto2[0], $hakemisto2[1], $kuvaus);
		    removeFromTable($hakemisto1[0], $hakemisto1[1], $kuvaus);
		    system("mv", $ARGV[1], $ARGV[2]);
		}
	    }
	}
	else {                                     #Jollei ole -i -optiota siirretään
	    if ($hakemisto1[1] eq $tiedosto) {     #kysymättä.
		addToTable($hakemisto2[0], $hakemisto2[1], $kuvaus);
		removeFromTable($hakemisto1[0], $hakemisto1[1], $kuvaus);
		system("mv", $ARGV[0], $ARGV[1]);
	    }
	}    
    }
    close(TAULUKKO);                                        #Suljetaan .comments.
}
# Kunkin käskyn jälkeen tutkitaan onko tässä hakemistossa sellaisia tiedostoja joille ei
# vielä ole annettu kuvausta. Jos niitä löytyy pyydetään käyttäjää antamaan kuvaus.

opendir(HAKEMISTO, ".");                  #Avataan hakemisto.                  
@tiedostot = readdir(HAKEMISTO);          #Luetaan hakemistossa olevat tiedostot
$i = 0;                                   #listaan.
open(TAULUKKO, ".comments");              #Avataan .comments -tiedosto.

while ($temp = <TAULUKKO>) {              #Luetaan .comments -tiedostoa.
    chop($temp);
    ($tiedosto, $kuvaus) = split(/\//, $temp, 2);
    @kommentoidut[$i++] = $tiedosto;      #Listataan ne tiedostot, joilla on jo kuvaus.
}
foreach $file (@tiedostot) {              #Merkitään ne tiedostot joilla on jo kuvaus.
    $flag = FALSE;
    foreach $tied (@kommentoidut) {
	if ($tied eq $file) {
	    $flag = TRUE;
	}
    }
    if ($flag ne TRUE) {                  #Pyydetään käyttäjältä kuvaukset niille
	                                  #tiedostoille joita ei vielä ole kuvattu.
	print "$file ei ole vielä kommentoitu.\nAnna kuvaus: ";
	$vastaus = <STDIN>;
	chop($vastaus);
	addToTable(".comments",$file,$vastaus);  # Lisätään kuvaukset .comments
    }                                            # -tiedostoon.
}
Esimerkkiajot:

Script started on Sat Mar 14 19:54:36 1998
leija 2_harkka % ./cls
.:   Tämän hetkinen hakemisto
..:   Äiti-hakemisto
ccp:   linkki ccp-ohjelmaan
cls:   linkki cls-ohjelmaan
cmv:   linkki cmv-ohjelmaan
crm:   linkki crm-ohjelmaan
testi_hakemisto:   Testausta varten luotu hakemisto
filedoc.pl:   Perl-ohjelma tiedostojen dokumentaatioon
filedoc.pl~:   emacsin luoma varmuuskopio
perli.gif:   Oma kuvatiedostotesti
.comments:   Tiedosto joka sisältää tiedostokuvaukset
.comments~:   emacsin luoma varmuuskopio
1kokeilu.txt:   Oma testifile
2testaus.txt:   Oma testifile
typescript ei ole vielä kommentoitu.
Anna kuvaus: Tiedosto jonne esimerkkiajo tallettuu
leija 2_harkka % ./cmv -i 1kokeilu.txt kokeilu1.txt
Move file 1kokeilu.txt to file kokeilu1.txt ? (y/n): y
leija 2_harkka % ./ccp -i perli.gif ~/kukka.gif
Copy file perli.gif to file /u/6/hppietil/kukka.gif ? (y/n): y
leija 2_harkka % ./crm -i perli.gif .comments~
File perli.gif. Remove ? (y/n): y
File .comments~. Remove ? (y/n): y
leija 2_harkka % ./ccp kettu.gif kana.gif
ccp: kettu.gif: No such file
leija 2_harkka % ./crm 2testaus.txt kokeilu1.txt
leija 2_harkka % ./cls
.:   Tämän hetkinen hakemisto
..:   Äiti-hakemisto
ccp:   linkki ccp-ohjelmaan
cls:   linkki cls-ohjelmaan
cmv:   linkki cmv-ohjelmaan
crm:   linkki crm-ohjelmaan
testi_hakemisto:   Testausta varten luotu hakemisto
filedoc.pl:   Perl-ohjelma tiedostojen dokumentaatioon
filedoc.pl~:   emacsin luoma varmuuskopio
.comments:   Tiedosto joka sisältää tiedostokuvaukset
typescript:   Tiedosto jonne esimerkkiajo tallettuu
leija 2_harkka % exit

script done on Sat Mar 14 19:58:30 1998
