(no subject)

Jun 24, 2006 00:54

После работы, катания на велосипеде, булки из подорожника и литра Кока-Колы я воодушевленно сел писать код. После часов двух экспериментов и чтения манов я добавил в свою программу, для учета финансов возможность обрабатывать опции коммандной строки. Использовал только getopts(), потому что getopts_long() не поддерживается POSIX'ом. Возможно, позже и прикручу это через #ifdef'ы, но пока такой надобности нет.

Собственно, чего я хотел-то?! Хотел показать свои наработки. Моя программа уже достигла символичного размера - 255 строк :) У меня много ещё идей, которые лишь предстоит реализовать, но то что есть сейчас уже работает и успешно мной используется.

Я не призываю вас использовать мою программу, (хотя это и не воспрещается ;) ) Я хотел бы услышать советы, аргументированную критику и просто ваши мнения. Представьте, что эта программа должна соответствовать всем стандартам, что она должна соответствовать идеальным стандартам по оформлению кода, что она должна работать всегда и везде, быть готовой к любым ситуациям. И теперь сравните это с моим кодом. Чего не хватает? Что можно улучшить?

Your welcome! ;-)



/*
* Open Finance Manager (OpenFM)
*
* Author: Semushin Slava

* Created: 23.04.2006
* Updated: 24.06.2006
* License: GPL
*
* */

/* for printf()
* fprintf()
* snprintf()
* sscanf()
* fopen()
* fgets()
* fclose()
* perror()
* FILE and NULL constants
* */
#include

/* for exit()
* malloc()
* free()
* getenv()
* EXIT_* constants
* */
#include

/* for strlen()
* */
#include

/* for getopt()
* */
#include

/* Name of file with data */
#define DATA_FILE "finance.db"

/* Maximum size of symbols in string without '\n' and '\0' */
#define MAX_STRING_LENGTH 70

/* Prototypes */
static void parse_cmd_line(int argc, char **argv);
static void print_help(void);
static void print_version(void);

/* Variables */
static int verbose = 0;
static char *__progname; /* initial below */
static char *__version = "0.5";

/*
* TODO:
* - separate code to functions
* - better English grammar
* - gettextize
*
* */

int main(int argc, char **argv) {
FILE *fd;
int ret;

char *dbfile;
char *homedir;
size_t filename_size;

char buffer[MAX_STRING_LENGTH + 2];
char sign;
float plus, minus, curr;
long lineno;

__progname = argv[0];

/* look at command line options */
parse_cmd_line(argc, argv);

homedir = getenv("HOME");
if (homedir == NULL) {
fprintf(stderr, "getenv: cannot get value for $HOME variable\n");
exit(EXIT_FAILURE);
}

if (verbose >= 1)
printf("-> Your home directory is '%s'\n", homedir);

/* Allocated memory for path to file with data:
* strlen("/home/coder" + "/" + "finance.db" + "\0")
* */
filename_size = strlen(homedir) + strlen(DATA_FILE) + 2;
dbfile = malloc(filename_size);
if (dbfile == NULL) {
fprintf(stderr, "malloc: cannot allocate memory\n");
exit(EXIT_FAILURE);
}

ret = snprintf(dbfile, filename_size, "%s/%s", homedir, DATA_FILE);
if ( (ret > 0) && ((size_t)ret >= filename_size) ) {
/*
* FIXME:
* - May be we should print more information ?
* - May occurs error when we cast ret to size_t type ?
*
* TODO:
* - NOTES on man page say then some version of glibc can
* return -1 when output was truncated. We should correct
* handle that case too.
*
* (BTW, is exist way to determine what glibc uses?)
*
* */
fprintf(stderr, "Writing data was truncated.\n"
"Please notify author about this incident!\n"
"snprintf: return value is %d\n", ret);
exit(EXIT_FAILURE);
}
if (ret < 0) {
fprintf(stderr, "Failed to write to the character string\n"
"snprintf: return value is %d\n", ret);
exit(EXIT_FAILURE);
}

/* open data file */
if (verbose >= 1)
printf("-> Open data file (%s)\n", dbfile);

fd = fopen(dbfile, "r");
if (fd == NULL) {
fprintf(stderr, "Failed to open file: %s\n", dbfile);
perror("fopen");
exit(EXIT_FAILURE);
}

/* free memory for path to data file */
free(dbfile);

if (verbose >= 1)
printf("-> Reading data...\n");

plus = minus = 0.0;
lineno = 0L;

/* read and parse data file */
/*
* FIXME:
* fgets() return NULL also when error occurs. We should correct
* handle this situation.
*
* */
while(fgets(buffer, MAX_STRING_LENGTH + 2, fd) != NULL){
lineno++;

if (verbose >= 3) {
/*
* FIXME:
* Just think what happens if now newline in buffer
*
* */
buffer[strlen(buffer) - 1] = '\0';
printf("---> %ld: '%s'\n", lineno, buffer);
}

/*
* TODO:
* - correctly handle return value from sscanf()
* - skip empty lines and lines which beginig with '#' (?)
* - add verification function
*
* */
(void)sscanf(buffer, "%c|%*2u.%*2u.%*4u|%f|%*s\n", &sign, &curr);
if (sign == '-') {
minus += curr;
continue;
}

if (sign == '+') {
plus += curr;
continue;
}

/* this code never happens */
}

if (verbose >= 1)
printf("-> Reads %ld strings from data file\n", lineno);

/* print short statistic */
printf("Finance statistic:\n"
"plus: %7.1f\n"
"minus: %7.1f\n" /* seven because point belongs to digital */
"balance: %7.1f\n",
plus, minus, plus - minus);

/* close data file */
ret = fclose(fd);
if (ret != 0) {
perror("fclose");
exit(EXIT_FAILURE);
}

return EXIT_SUCCESS;
}

static void print_help(void) {
printf("%s: Your private finance manager\n\n"
"Usage: %s [option]\n"
" -v\tenable verbose mode\n"
" -V\tprint version and exit\n"
" -h\tprint this help and exit\n",
__progname, __progname);

exit(EXIT_SUCCESS);
}

static void print_version(void) {
printf("%s: version %s\n"
"Copyright (C) 2006 Slava Semushin
\n",
__progname, __version);

exit(EXIT_SUCCESS);
}

static void parse_cmd_line(int argc, char **argv) {
int option;

while ((option = getopt(argc, argv, "vVh")) != -1) {
switch (option) {
case 'v': /* enable verbose mode */
verbose++;
break;
case 'V': /* print version of program and exit */
print_version();
break;
case 'h': /* print help and exit */
print_help();
break;
case '?': /* for unknown options */
exit(EXIT_FAILURE);
default: /* that case never happens */
fprintf(stderr, "getopt: return %c\n", option);
break;
}
}

}

_Winnie C++ Colorizer

Updated(20060625): added tag ofm
Updated(20060625): changed tag ofm to openfm

programming, code, openfm

Previous post Next post
Up