diff --git a/cpp/aimaina/Makefile b/cpp/aimaina/Makefile new file mode 100644 index 0000000..4a76752 --- /dev/null +++ b/cpp/aimaina/Makefile @@ -0,0 +1,12 @@ +OBJS = aimaina.o book.o search.o util.o + +aimaina:$(OBJS) + $(CC) -o $@ $^ + +.c.o: + $(CC) -c $< + +aimaina.o: aimaina.c util.h book.h +book.o: book.c util.h search.h book.h +search.o: search.c search.h +util.o: util.c diff --git a/cpp/aimaina/README.md b/cpp/aimaina/README.md new file mode 100644 index 0000000..0746d0a --- /dev/null +++ b/cpp/aimaina/README.md @@ -0,0 +1,14 @@ +蔵書管理プログラム aimaina +========================= + +できること +-------- +- 書籍情報の追加/検索/削除 +- 書籍情報を"booklist.csv"に記録 +- プログラミングIIの単位を取る + +ライセンス +-------- +©2012 [@kou029w](http://twitter.com/kou029w) - [MIT license][MIT] + +[MIT]: http://kou029w.appspot.com/mit-license.txt \ No newline at end of file diff --git a/cpp/aimaina/aimaina.c b/cpp/aimaina/aimaina.c new file mode 100755 index 0000000..c96c334 --- /dev/null +++ b/cpp/aimaina/aimaina.c @@ -0,0 +1,31 @@ +/* +aimaina.c +蔵書管理プログラム +(C)2012 B11T3074C, B11T3011K - MIT License +*/ + +#include +#include +#include +#include "util.h" +#include "book.h" + +int main(){ + char buff[1024]; + openlist("booklist.csv"); + for(;;){ + printf("追加[a] 検索[s] 終了[q] "); + input_line(buff, 1024); + strtolower(buff); + + if(strcmp(buff, "a") == 0){ + regist_data(); + }else if(strcmp(buff, "s") == 0){ + search(); + }else if(strcmp(buff, "q") == 0){ + savelist("booklist.csv"); + exit(0); + } + } + return 0; +} \ No newline at end of file diff --git a/cpp/aimaina/aimaina.o b/cpp/aimaina/aimaina.o new file mode 100644 index 0000000..133aded Binary files /dev/null and b/cpp/aimaina/aimaina.o differ diff --git a/cpp/aimaina/book.c b/cpp/aimaina/book.c new file mode 100644 index 0000000..99b3bf3 --- /dev/null +++ b/cpp/aimaina/book.c @@ -0,0 +1,239 @@ +/* +book.c +蔵書管理のための関数群 +(C)2012 B11T3074C, B11T3011K - MIT License +*/ + +#include +#include +#include +#include "util.h" +#include "search.h" +#include "book.h" + +// リストの先頭アドレス +Bookdata *booklist_head = NULL; + +// キーボードから本のデータを入力 +void input_data(Bookdata *target){ + target->title = input_string("タイトル>", target->title); + target->author = input_string("著者>", target->author); + target->publisher = input_string("出版元>", target->publisher); + target->year = input_string("発行年>", target->year); + target->pages = input_string("ページ数>", target->pages); + target->isbn = input_string("ISBN>", target->isbn); + target->note = input_string("備考>", target->note); +} + +// 本のデータを表示 +void show_data(Bookdata *target){ + printf("タイトル:\t%s\n", target->title); + printf("著者:\t\t%s\n", target->author); + printf("出版元:\t\t%s\n", target->publisher); + printf("発行年:\t\t%s\n", target->year); + printf("ページ数:\t%s\n", target->pages); + printf("ISBN:\t\t%s\n", target->isbn); + printf("備考:\t\t%s\n", target->note); +} + +// 本のリストからデータを削除 +void delete_data(Bookdata *target){ + if(target->prev == NULL && target->next == NULL){ + // 最後の1冊の場合 + booklist_head = NULL; + }else if(target->prev == NULL){ + // 先頭の場合 + booklist_head = target->next; + (target->next)->prev = NULL; + }else if(target->next == NULL){ + // 末尾の場合 + (target->prev)->next = NULL; + }else{ + (target->next)->prev = target->prev; + (target->prev)->next = target->next; + } + + free(target->title); // タイトル + free(target->author); // 著者 + free(target->publisher); // 出版元 + free(target->year); // 発行年 + free(target->pages); // ページ数 + free(target->isbn); // ISBN + free(target->note); // 備考 + free(target); +} + +// リストの末尾にnewdataを追加 +void add_data(Bookdata *new_data){ + Bookdata *tail; + + new_data->next = NULL; + + if(booklist_head == NULL) { + // 最初の1冊の場合 + booklist_head = new_data; + new_data->prev = NULL; + return; + } + + // 末尾を検索 + for(tail = booklist_head; tail->next != NULL; tail = tail->next); + + // 末尾の後ろにつなぐ + tail->next = new_data; + new_data->prev = tail; +} + +// リストをすべて表示 +void show_all(){ + Bookdata *tail; + + puts("---"); + for(tail = booklist_head; tail != NULL; tail = tail->next){ + show_data(tail); + puts("---"); + } +} + +// リストをCSVで保存 +void savelist(char *filename){ + FILE *fp; + Bookdata *tail; + + // filenameが""ならば標準出力をオープンする + if(filename[0] == '\0'){ + fp = stdout; + }else{ + fp = fopen(filename, "w"); + } + + fputs("title, author, publisher, year, pages, isbn, note\n", fp); + for(tail = booklist_head; tail != NULL; tail = tail->next){ + fputs(tail->title, fp); // タイトル + fputs(", ", fp); + fputs(tail->author, fp); // 著者 + fputs(", ", fp); + fputs(tail->publisher, fp); // 出版元 + fputs(", ", fp); + fputs(tail->year, fp); // 発行年 + fputs(", ", fp); + fputs(tail->pages, fp); // ページ数 + fputs(", ", fp); + fputs(tail->isbn, fp); // ISBN + fputs(", ", fp); + fputs(tail->note, fp); // 備考 + fputs("\n", fp); + } + fclose(fp); +} + +// リスト(CSVファイル)を開く +int openlist(char *filename){ + char buff[7][1024]; + int i, j; + FILE *fp; + + // filenameが空なら標準入力をオープンする + if(filename[0] == '\0'){ + fp = stdin; + }else{ + fp = fopen(filename, "r"); + } + + if(fp == NULL){ + fprintf(stderr, "\"%s\"が開けません\n", filename); + return -1; + } + + // 1行目はスルー + fscanf(fp, "%[^\n]\n", buff[6]); + + while(EOF != fscanf(fp, "%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^\n]\n", + buff[0], buff[1], buff[2], buff[3], buff[4], buff[5], buff[6])){ + Bookdata book; + Bookdata *bp; + + for(i=0; i<7; i++){ + // ","の後のスペースを削除 + if(buff[i][0] == ' ') strcpy(buff[i], &buff[i][1]); + } + + book.title = strdup(buff[0]); + book.author = strdup(buff[1]); + book.publisher = strdup(buff[2]); + book.year = strdup(buff[3]); + book.pages = strdup(buff[4]); + book.isbn = strdup(buff[5]); + book.note = strdup(buff[6]); + bp = malloc(sizeof(Bookdata)); + *bp = book; + add_data(bp); + } + fclose(fp); + return 0; +} + +// bookを初期化 +void init_data(Bookdata *book){ + book->title = strdup(""); + book->author = strdup(""); + book->publisher = strdup(""); + book->year = strdup(""); + book->pages = strdup(""); + book->isbn = strdup(""); + book->note = strdup(""); +} + +// 追加する +void regist_data(){ + Bookdata tmp; + Bookdata *tail; + init_data(&tmp); + input_data(&tmp); + tail = malloc(sizeof(Bookdata)); + *tail = tmp; + add_data(tail); +} + +// 検索する +void search(){ + char buff[1024]; + char pattern[1024]; + Bookdata *tail; + Bookdata *matches[1024]; + int count; + int i; + + printf("/"); + input_line(pattern, 1024); + + count = 0; + for(tail = booklist_head; tail != NULL; tail = tail->next){ + if(smatch(pattern, tail->title) + |smatch(pattern, tail->author) + |smatch(pattern, tail->publisher) + |smatch(pattern, tail->note)){ + puts("---"); + matches[count++] = tail; + show_data(tail); + } + } + + if(count <= 0) return; // 見つからないので、戻る + puts("---"); + printf("削除[d] 戻る[x] "); + input_line(buff, 1024); + strtolower(buff); + + if(strcmp(buff, "d") == 0){ + printf("削除します。よろしいですか? [y/N] "); + if(select_y('N')){ + for(i=0; i +#include +#include "search.h" + +// stringにpatternが含まれていれば1、それ以外で0を返す +int match(char *pattern, const char *string){ + const char *p; + size_t pat_len = strlen(pattern); + for(p=string; *p; p++){ + if(!strncmp(pattern, p, pat_len)){ + return 1; + } + } + + return 0; +} + +// 破壊的AND-match : "pattern1 pattern2" => "pattern1" AND "pattern2" +int andmatch(char *pattern, const char *string){ + char *p; + int match_flag = 1; + + for(p = strtok(pattern, " "); p != NULL; p = strtok(NULL, " ")){ + match_flag &= match(p, string); + } + + return match_flag; +} + +// 破壊的OR-match : "pattern1|pattern2" => "pattern1" OR "pattern2" +int ormatch(char *pattern, const char *string){ + char *p; + int match_flag = 0; + + for(p = strtok(pattern, "|"); p != NULL; p = strtok(NULL, "|")){ + match_flag |= match(p, string); + } + + return match_flag; +} + +// 再帰可能なstrtok +char *strtok_r(char *str, const char *delim, char **saveptr){ + size_t len; + char *head; + + if(str == NULL){ + head = *saveptr; + }else{ + head = str; + } + len = strcspn(head, delim); + if(len == 0){ + head += strspn(head, delim); + len = strcspn(head, delim); + } + if(*head == '\0'){ + return NULL; + } + + *saveptr = head + len + strspn(head + len, delim); + *(head + len) = '\0'; + + return head; +} + +// Smart-match : 破壊的でない +// "pattern1 pattern2|pattern3" => "pattern1" AND ("pattern2" OR "pattern3") +int smatch(const char *pattern, const char *string){ + char *p; + char *nextp; + int match_flag; + char *tmp; + + tmp = strdup(pattern); + match_flag = 1; + for(p = strtok_r(tmp, " ", &nextp); p != NULL; p = strtok_r(NULL, " ", &nextp)){ + match_flag &= ormatch(p, string); + if(match_flag == 0) break; + } + free(tmp); + + return match_flag; +} diff --git a/cpp/aimaina/search.h b/cpp/aimaina/search.h new file mode 100644 index 0000000..d93fc12 --- /dev/null +++ b/cpp/aimaina/search.h @@ -0,0 +1,16 @@ +/* +search.h +検索するための関数群 +(C)2012 B11T3074C, B11T3011K - MIT License +*/ + +#ifndef SEARCH_H +#define SEARCH_H + +int match(char *pattern, const char *string); +int andmatch(char *pattern, const char *string); +int ormatch(char *pattern, const char *string); +char *strtok_r(char *str, const char *delim, char **saveptr); +int smatch(const char *pattern, const char *string); + +#endif \ No newline at end of file diff --git a/cpp/aimaina/search.o b/cpp/aimaina/search.o new file mode 100644 index 0000000..72511b9 Binary files /dev/null and b/cpp/aimaina/search.o differ diff --git a/cpp/aimaina/util.c b/cpp/aimaina/util.c new file mode 100644 index 0000000..ec8b175 --- /dev/null +++ b/cpp/aimaina/util.c @@ -0,0 +1,71 @@ +/* +util.c +文字列操作のための関数群 +(C)2012 B11T3074C, B11T3011K - MIT License +*/ + +#include +#include +#include +#include + +// 文字列版tolower() +char *strtolower(char *s){ + char *p; + + for(p=s; *p != EOF; p++){ + *p = tolower(*p); + } + return s; +} + +// 1行入力 +void input_line(char *buff, size_t size){ + char *p; + if(fgets(buff, size, stdin) == NULL){ + exit(1); + } + // 改行コードを削除 + for(p=buff; *p != '\n'; p++); + *p = '\0'; +} + +// "y", "n"で選択("n"ならば0を返す) +int select_y(char ch){ + char buff[1024]; + + for (;;){ + input_line(buff, 1024); + strtolower(buff); + + if (strcmp(buff, "y") == 0){ + return 1; + }else if (strcmp(buff, "n") == 0){ + return 0; + }else{ + switch(ch){ + case 'y': + case 'Y': + return 1; + case 'n': + case 'N': + return 0; + } + fputs("yまたはnを入力してください\n", stderr); + } + } +} + +// 文字列を入力(defstr:入力がないときデフォルトで返す文字列) +char *input_string(char *prompt, char *defstr){ + char buff[1024]; + + printf("%s", prompt); + input_line(buff, 1024); + if(buff[0] != '\0'){ + // 空でない場合 + free(defstr); + return strdup(buff); + }; + return defstr; +} diff --git a/cpp/aimaina/util.h b/cpp/aimaina/util.h new file mode 100644 index 0000000..be347c7 --- /dev/null +++ b/cpp/aimaina/util.h @@ -0,0 +1,15 @@ +/* +util.h +文字列操作のための関数群 +(C)2012 B11T3074C, B11T3011K - MIT License +*/ + +#ifndef UTIL_H +#define UTIL_H + +char *strtolower(char *s); +void input_line(char *buff, size_t size); +int select_y(char ch); +char *input_string(char *prompt, char *defstr); + +#endif \ No newline at end of file diff --git a/cpp/aimaina/util.o b/cpp/aimaina/util.o new file mode 100644 index 0000000..f31f4c0 Binary files /dev/null and b/cpp/aimaina/util.o differ