C-ohjelmoinnin harjoitustyö

C-ohjelmoinnin harjoitustyö syksyltä 1994. Tämä ohjelma ilmeisesti … tekee jotakin :-D

/*

C-Ohjelmointi, syksy 1994
Heikki Siltala
Harjoitustyo, tehtava 5

Ohjelman kaytto:

NIMI tiedosto1 tiedosto2 sivunpituus

Kaikki parametrit on aina annettava !!!

Tiedosto1 on tiedosto, jossa olevien sanojen mukaan lista laaditaan.
Tiedosto2 on tiedosto, josta katsotaan sanojen esiintymispaikat.
Sivunpituus ilmoittaa, montako rivia on sivussa. Jos tama parametri
on <=0 kaytetaan oletusarvoa.

Halutessasi kayttaa samaa tiedostoa niin sanojen listaanluvussa kuin
niiden esiitymisien etsinnassakin, syota sama tiedostonimi kahteen
kertaan!
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

/*
Joukko makroja, jotka ilmoittavat virhetilanteista ja keskeyttavat
ohjelman siististi:
*/

#define MUISTI_LOPPUI\
        do\
	{\
	      printf("Muisti loppui. Ohjelma keskeytyy...\n\a");\
              exit(1);\
        }\
        while(0)

#define TIEDOSTOVIRHE\
        do\
        {\
              printf("Tiedostoa ei voida avata. Ohjelma keskeytyy...\n\a");\
              exit(1);\
        }\
        while(0)

#define EI_SANOJA\
        do\
        {\
              printf("Tiedostossa ei ole sanoja. Ohjelma keskeytyy...\n\a");\
              exit(1);\
        }\
        while(0)

#define PARAMETRIVIRHE\
        do\
	{\
	      printf("Ole hyvä, anna oikea määrä parametreja (3 kpl).\n\a");\
	      printf("Ohjelma keskeytyy...\n");\
	      exit(1);\
	}\
	while(0)

/*
Jos tama lauseke on TRUE, c on 'valimerkki'
*/

#define SPACE (c=='\n'||c=='\t'||c==' ')  /* (!(isprint(c))) ??? */

#define SANANPITUUS 40   /* Sanan maksimipituus */
#define SIVUNPITUUS 24   /* Sivunpituuden oletusarvo */

/* Seuraavalla makrolla varataan muistia: */

#define M(X) calloc(1,sizeof(struct X))


/*
Ohjelmassa kaytettavat listarakenteet:

Struct solmu on rakenne, jossa on sana (merkkijono), osoitin
seuraavan sanaan ja osoitin listaan, jossa on taman sanan
ilmenemispaikkojen rivinumerot.

Sturct alkio on rakenne, jossa on ilmenemispaikan rivinumero
ja osoitin seuraavaan ilmenemispaikkaan.
*/

struct alkio
{
    int sij;
    struct alkio *et;
};

struct solmu
{
    char teksti[SANANPITUUS+1];
    struct solmu *seur;
    struct alkio *alku;
};

/*
Seuraava funktio lukee sanan tiedostosta p, tallettaa tuloksen
muuttujaan data ja kasvattaa lf-muuttujaa rivinvaihtojen
määrällä. Huomaa: muuttuja tila ilmaisee ollaanko sanan sisalla
vai ulkopuolella. Lukeminen tapahtuu merkki kerrallaan.

Funktio palauttaa arvon 1 jos luettiin sana, arvon 2 jos sana
jouduttiin katkaisemaan ja arvon 0 jos sanaa ei voitu lukea.

Huomaa, etta jos sana jouduttiin katkaisemaan, jaljelle jaaneet
merkit luetaan pois.
*/

int
lue_sana(FILE *p, int *rivit, char *data, const int max)
{
    int tila=0;
    int lask=0;
    register int c;

    memset(data,'\0',((sizeof(char))*(max+1)));

    if(feof(p))
	return 0;

    while((c=fgetc(p))!=EOF)
    {
	switch(tila)
	{
	    case 0:
		if(SPACE)
		{
		    if(c=='\n')
			(*rivit)++;
                    if(feof(p))
                        return 0;
                }
		else
		{
		    tila=1;
		    data[lask++]=c;
		}
		break;

	    case 1:
		if(SPACE)
		{
		    data[lask]='\0';
		    if(c=='\n')
                        ungetc(c,p);
		    return 1;
		}
		else
		{
		   data[lask++]=c;
		   if(lask==max)
		   {
		       data[lask]='\0';
		       while((!(SPACE))&&(!(feof(p))))
			   c=fgetc(p);
		       if(c=='\n')
                           ungetc(c,p);
		       return 2;
		   }
		}
		break;
	}
    }
    data[lask]='\0';
    return 1;
}

/*
Seuraava funktio luo uuden solmun. Se saa parametrina merkkijonon,
joka sijoitetaan solmuun. Solmun osoite palautetaan.
*/

struct solmu *
tee(const char *tieto)
{
    struct solmu *x;
    x=M(solmu);
    if(x==NULL)
	MUISTI_LOPPUI;
    strcpy(x->teksti,tieto);
    x->alku=NULL;
    return x;
}

/*
Seuraava funktio lisaa sanan oikealle paikalleen listaan, jonka
alku annetaan muuttujassa lista. Jos listasta loytyy sama sana, ei
tehda mitaan. Funktio palauttaa osoittimen listan alkuun.
*/

struct solmu *
sana_listaan(struct solmu *lista, char *data)
{
    int t;
    struct solmu *a,*b,*pt;

    if(!lista)                /* Jos lista on tyhja... */
	return tee(data);

    pt=lista;
    while(pt)
    {
	t=strcmp(pt->teksti,data);

	if(t==0)              /* Jos sana on jo ennestaan... */
	    return lista;

	if(t>0)   /* < ??? */
	{

	    if(pt==lista)
	    {
		a=tee(data);
		a->seur=lista;
		return a;
	    }

	    a=tee(data);
	    b=lista;
	    while(b->seur!=pt)
		b=b->seur;
	    b->seur=a;
	    a->seur=pt;
	    return lista;
	}

	pt=pt->seur;
    }
    pt=lista;                   /* Jos sanan paikka on listan lopussa... */
    while(pt->seur)
	pt=pt->seur;
    a=tee(data);
    pt->seur=a;
    a->seur=NULL;
    return lista;
}

/*
Tama funktio laittaa sanan ilmenemispaikan listaan. Ilmenemispaikka
= rivinumero, joka saadaan parametrissa rvt.
*/

void
paikka_listaan(const char *data, const int rvt, struct solmu *lista)
{
    struct solmu *os;
    struct alkio *ak,*uusi;

    if(lista)     /* Jos lista ei ole tyhja... */
    {
	os=lista;

                  /* Etsitaan sana listasta... */
        while((os!=NULL)&&(strcmp(data,os->teksti)!=0))
            os=os->seur;

	if(os)                /* Jos loytyi lisataan paikka... */
        {
            ak=os->alku;
	    if(!ak)           /* ...ekaksi jos ilmenemispaikkalista */
            {                                      /* on tyhja ...  */
		uusi=M(alkio);
                os->alku=uusi;
                uusi->sij=rvt;
                uusi->et=NULL;
            }
            else
	    {
	       while(ak->et)   /* ...tai muutoin loppuun. */
                  ak=ak->et;
	       uusi=M(alkio);
	       ak->et=uusi;
	       uusi->sij=rvt;
               uusi->et=NULL;
            }
        }
    }
}

/*
Tama funktio kay lapi listan (parametrissa aletaan) ja tulostaa siina
olevat sanat esiintymispaikkoineen, formaatti on esim:

SANA
sivu rivi      sivu rivi     sivu rivi    sivu rivi

SANA
sivu rivi

SANA

SANA
sivu rivi      sivu rivi
*/

void
tulosta(const struct solmu *aletaan, const int sivpit)
{
    struct solmu *o;
    struct alkio *p;
    int s,r;

    o=aletaan;

    while(o)
    {
	printf("%s\n",o->teksti);
	p=o->alku;
	while(p)
	{
            s=(((p->sij)-1)/sivpit)+1;
            r=(((p->sij)%sivpit)!=0)?((p->sij)%sivpit):sivpit;
	    printf("%d %d\t",s,r);
	    p=p->et;
				  /* Lasketaan mille sivulle ja sen */
	}                         /* riville sana osuu, aika hassut */
	printf("\n\n");           /* lausekkeet...? */
	o=o->seur;
    }
}

int
main(int argc, char *argv[])
{
    /*
    MUUTTUJAT:
    fp: tiedosto-osoitin,
    katk: pitaa lukua kuinka monta sanaa on katkaistu,
    lkm: pitaa lukua sanoista,
    rivi: pitaa ylhaalla milla rivilla ollaan menossa,
    sivu: sisaltaa sivunpituuden arvon,
    tilanne: ottaa vastaan lue_sana-funktion palauttaman
	     arvon myohempaa tarkastelua varten,
    sana: tahan merkkijonoon luetaan sana, pituudesta
	  on viimeinen merkki varattu '\0' varten,
    eka: osoittaa koko ajan sanalistan alkuun.
    */

    FILE *fp;
    int katk,lkm,rivi,sivu,tilanne;
    char sana[SANANPITUUS+1];
    struct solmu *eka=NULL;

    if(argc<4)
       PARAMETRIVIRHE;

    sivu=strtol(argv[3],NULL,10);   /* Sivunpituus saa arvon */
    if(sivu<=0)                     /* SIVUNPITUUS jos parametri */
	sivu=SIVUNPITUUS;           /* <=0. */

    fp=fopen(argv[1],"r");
    if(fp==NULL)
	TIEDOSTOVIRHE;

    katk=lkm=0;
    rivi=1;
    tilanne=lue_sana(fp,&rivi,sana,SANANPITUUS);
    while(tilanne!=0)
    {
	if(tilanne==2)                        /* Luetaan sanat listaan */
	    katk++;
	lkm++;
	eka=sana_listaan(eka,sana);
	tilanne=lue_sana(fp,&rivi,sana,SANANPITUUS);
    }

    if(lkm==0)
	EI_SANOJA;

    printf("Vaihe 1, sanojen luku listaan:\n");
    printf("Luettiin %d sana(a), %d katkaisiin.\n",lkm,katk);

    fclose(fp);

    fp=fopen(argv[2],"r");
    if(fp==NULL)
	TIEDOSTOVIRHE;

    lkm=katk=0;
    rivi=1;
    tilanne=lue_sana(fp,&rivi,sana,SANANPITUUS);
    while(tilanne!=0)
    {
	if(tilanne==2)
	    katk++;
	lkm++;
	paikka_listaan(sana,rivi,eka);
	tilanne=lue_sana(fp,&rivi,sana,SANANPITUUS);
    }
    fclose(fp);

    if(lkm==0)
	EI_SANOJA;
    printf("Vaihe 2, sanojen esiintymispaikkojen kartoitus:\n");
    printf("Luettiin %d sana(a), %d katkaistiin.\n\n",lkm,katk);

    printf("Sanojen esiintymat:\n");
    tulosta(eka,sivu);      /* Tulostetaan sanat ja paikat. */

    return 0;
}

/*
Heikki Siltalan harjoitustyon LOPPU
*/