IZP Projekt 1

Zdrojový kód prvního projektu do předmětu IZP. Jde o jednoduché šifrování textu podle zadaného klíče. Více v příslušném článku nebo přímo v komentářích v kódu.

/*
 * Soubor:  proj1.c
 * Datum:   21.10.2008
 * Autor:   David Sabata (xsabat01)
 * Projekt: Sifrovani, projekt c. 1 pro predmet IZP
 * Popis:   Program provadi de/sifrovani vstupniho textu podle zadaneho klice
 */
 
#include 
#include 
#include 
 
// "Cislice" nasi soustavy; dalo by se prepocitavat z ASCII, ale v ramci
// rychlosti a rozsiritelnosti radeji pouzivam pole
const char TRANS[] = {'0','1','2','3','4','5','6','7','8','9',
                    'A','B','C','D','E','F','G','H','I','J','K','L','M','N',
                    'O','P','Q','R','S','T','U','V','W','X','Y','Z'};
// Zaklad soustavy
const int BASE = 36;
 
// Ukonceni radku
const int CR = 13;
const int LF = 10;
 
/**
 * Chybove hlasky
 */
const char *EMSG[] =
{
   "Vse v poradku",
   "Nespravne parametry. Prosim, pouzijte napovedu.",
   "Neplatny znak klice",
   "Neplatny vstupni znak"
};
 
/**
 * Vysledky rozboru argumentu
 */
enum actions
{
   AHELP,   // budeme tisknout napovedu
   AENCODE, // budeme sifrovat
   ADECODE  // bdueme desifrovat
};
 
/** Funkce **/
int loadParams(int argc, char *argv[]);
int charOk(int ch);
int encdec(int action, int ch1, int ch2);
int getKeyChar(char *argv, int keyIterator);
void ltr2num(int *ch);
void num2ltr(int *ch);
void printError(int error);
void printHelp();
 
/******************************************************************************/
 
/**
 * Main
 */
int main(int argc, char *argv[])
{
   // Akce urcena na zaklade parametru
   int action = loadParams(argc, argv);
 
   // Znacitko aktualniho znaku ktery z klice bereme (pro posouvani)
   int keyIterator = 0;
 
   // Delka retezce klice
   int keyLength = 0;
 
   // Dva znaky se kteryma se bude pracovat
   int vstup = 0;
   int klic = 0;
 
   // Bude se pouzivat pro podrzeni zpracovaneho znaku predtim nez se vytiskne
   char out;
 
   switch (action) {
      // Vytisknuti napovedy
      case AHELP:
         printHelp();
         break;
      // De/sifrovani
      case AENCODE:
      case ADECODE:
         // Spocitani delky klice, at se nepocita v cyklu
         keyLength = strlen(argv[2]);
 
         do {
            // nacist znak z klice
            klic = getKeyChar(argv[2], keyIterator);
            // posuneme ukazatel na nasledujici znak a pokud jsme na konci
            // retezce klice, skocime zase na zacatek
            keyIterator++;
            if (keyIterator==keyLength) keyIterator = 0;
 
            // nacist znak ze vstupu
            vstup = getchar();            
 
            // spravny znak vypsat, pokud neni spravny, nastavila se chyba
            if (charOk(vstup) && klic>0) {
               // Zpracovat znak prislusnou akci a vypsat
               out = encdec(action, vstup, klic);
               putchar(out);
            }
 
         // Nacitani dalsich klaves dokud nenarazime na konec vstupu
         } while (vstup>0 && vstup!=CR && vstup!=LF);
   }      
 
   return EXIT_SUCCESS;
}
 
/**
 * Nacte parametry a vyhodnoti akci, pripadne chybu
 * @param argc int pocet parametru
 * @param argv char pole parametru
 * @return int akce ktera se ma provest
 */
int loadParams(int argc, char *argv[])
{
   // Napoveda
   if (argc == 2 && strcmp("-h", argv[1]) == 0)
      return AHELP;
 
   // Sifrovani
   if (argc == 3 && strcmp("-encode", argv[1]) == 0)
      return AENCODE;
 
   // Desifrovani
   if (argc == 3 && strcmp("-decode", argv[1]) == 0)
      return ADECODE;
 
   // Cokoliv jineho je chyba, vytiskneme hlasku o nespravnych parametrech,
   // coz zaroven ukonci program. Hodnota v returnu je pouze formalita.
   printError(1);
 
   return -1;
}
 
/**
 * Vytiskne chybove hlaseni na chybově věstup. Pote tvrde ukonci program, asi by
 * bylo praktictejsi nechat chybu vybublat az do fce main, ale jelikoz mame
 * zakazano pouziti globalnich promennych, bylo by slozite to realizovat, proto
 * se vola ukonceni hned pri vypsani chyby.
 * @param error int cislo chybove hlasky
 */
void printError(int error)
{
   fprintf(stderr, "Chyba: %s\n", EMSG[error]);
   exit(EXIT_FAILURE);
}
 
/**
 * Vytiskne napovedu
 */
void printHelp()
{
   printf(
      "Program Sifrovani\n"
      "Autor: David Sabata (c)2008\n"
      "Program de/sifruje text ze standardniho vstupu pomoci klice zadaneho \n"
      "v parametru. Pro klic i vstupni text jsou povolena "
      "pouze velka pismena anglicke abecedy a cisla.\n"
      "Parametry:\n"
      "\t-encode KLIC\tprogram bude sifrovat podle klice KLIC\n"
      "\t-decode KLIC\tprogram bude desifrovat podle klice KLIC\n"
      "\t-h\t\tzobrazeni teto napovedy\n\n"
   );
}
 
/**
 * Kontrola znaku - bereme pouze velke pismena a cisla, porovnavame podle ascii
 * @param ch int znak ke kontrole
 * @return int [0|1]
 */
int charOk(int ch)
{
   // Osetreni EOL a EOF - je to chybovy znak, ale nevypisujeme hlasku
   if ( ch==CR || ch==LF || ch<0 ) return 0;
   // Konrola podle cisla znaku v ASCII
   if ( (ch>='0' && ch< ='9') || (ch>='A' && ch< ='Z') ) return 1;
   else
      // nastavit "chyba na vstupu"
      printError(3);
 
   return 0;
}  
 
/**
 * De/sifrovani znaku
 * @param action int akce (AENCODE|ADECODE)
 * @param ch1 int prvni znak k zasifrovani
 * @param ch2 int druhy znak k zasifrovani
 * @return int de/sifrovany znak
 */
int encdec(int action, int ch1, int ch2)
{
   // Prevest na desitkove cislo
   ltr2num(&ch1);
   ltr2num(&ch2);
 
   // Vysledny znak
   int chr;
 
   if (action == AENCODE) {
      // Sifrovani je proste secteni
      chr = ch1 + ch2;
   } else {
      // Pricist zaklad, ktery vyvazi chybu zanedbavani
      // vyssich radu pri sifrovani; vysledek to nepokazi, protoze vystup
      // jde pres %BASE (%36);
      ch1 += BASE;
 
      // Desifrovani je odcitani klice
      chr = ch1 - ch2;  
 
      // Osetrit zaporny rozdil - nahrada funkce fabs()
      if (chr < 0)
         chr *= -1;
   }   
 
   // Prevest zpet na kod
   num2ltr(&chr);   
 
   return chr;
}
 
/**
 * Vraci jeden znak z klice kterym (de)kodujeme
 * @param char key klic podle ktereho se sifruje
 * @param int keyIterator poradi znaku v retezci, se kterym se bude pracovat
 * @return int znak z klice
 */
int getKeyChar(char *key, int keyIterator)
{
   // nacist znak
   char ch = key[keyIterator];
 
   // zkontrolovat ho podle cisla v ASCII, popr. vypsat chybu a ukoncit
   if ( ch<'0' || (ch>'9' && ch< 'A') || ch>'Z' ) {
      printError(2);
   } 
 
   return ch;
}  
 
/**
 * Prevadi znak na desitkove cislo 0-35
 * @param int ch znak k upraveni
 */
void ltr2num(int *ch)
{
   // Cisla jsou cisla, namisto pouziti fce pro prevod, jednoduse odecteme
   // ASCII hodnotu znaku 0, cimz prevedeme cislo z jeho ASCII hodnoty na
   // hodnotu kterou potrebujeme
   if (*ch >= '0' && *ch < = '9') {
      *ch = *ch - '0';
   }
 
   // Pismena se prepocitaji obdobne
   if (*ch >= 'A' && *ch < = 'Z') {
      // Odecteme ASCII hodnotu znaku A, cimz pro nas budou pismena A,B,C,...
      // predstavovat hodnoty 0,1,2,...
      *ch = *ch - 'A';
      // Pricteme 10, coz znamena, ze A je na 10. pozici v tabulce, vyplyva to
      // z toho, ze je pred nim 10 cislic [0-9]; nepouzivam zde zadnou konstantu
      // protoze jde o jediny vyskyt v programu a tezko budeme nekdy pouzivat
      // vic cislic nez 10
      *ch += 10;
   }
 
}   
 
/**
 * Prevadi cislo 0-35 na odpovidajici znak
 * @param int ch cislo k upraveni
 */
void num2ltr(int *ch)
{
   // Modulo zajisti, ze cislo nebude vetsi nez zaklad soustavy
   *ch = *ch % BASE;
   // a z tabulky jednoduse vybereme znak na odpovidajici pozici
   *ch = TRANS[*ch];
}

Comments are closed.