강좌 첫화면으로 최근 글 보기(Post)
갈래별로 보기 categories




  52. 텍스트 프로그램 예제



52. 텍스트 프로그램 예제


이번 장부터 몇 가지 응용 프로그램을 예제로 다루겠습니다. 한 가지 밝혀둘 점은 이 책에서 사용한 예제 프로그램 중에는 제가 만든 것도 있지만 대부분 다른 분들이 만들어서 공개한 프로그램이라는 점입니다. 제가 한 일은 다른 분들이 만든 프로그램을 좀더 보기 편하게 손을 본 정도입니다.

특히 그래픽 프로그램 부분의 소스파일은 상당부분 PC통신망에 올라온 다른 분들의 소스파일을 참고하였습니다. 그러나 워낙 오래 전에 소스파일들을 갈무리해두었고, 제 프로그램에 응용하면서 일부분을 수정하고 필요 없는 부분을 삭제하다보니 맨 처음 이 프로그램을 올린 분이 누구인지 알지 못합니다. 또한 통신상으로 올라온 소스파일이 대부분 몇 차례의 갈무리과정과 수정과정을 거쳐서 제 손에 들어온 것이라서 애초 제가 처음 소스파일을 구했을 때부터 원작자가 누구인지 알 수 없는 상태인 경우가 많습니다.

다만 pcx파일을 보여주는 프로그램 등 몇 개의 프로그램은 제가 아는 분들이 저의 부탁을 받고 제공해준 프로그램입니다. 그런데 그때는 이 책을 쓸 것이라고 생각하지 않았기에 그냥 고맙다는 인사말만 하고 냉큼 소스파일을 받았습니다. 그래서 제게 소스파일을 제공해주신 그분들의 이름을 지금 기억하지 못하는데, 그분들께 죄송할 뿐입니다. 분명한 것은 제가 이 책에서 사용한 소스파일의 상당수(아마 대부분일 겁니다.)는 하이텔의 OSC동아리와 두루물 회원들의 도움으로 얻었거나 그분들이 짜신 것을 제가 부탁하여(다른 말로 구걸하여) 얻어낸 소스파일이라는 점입니다. 하이텔의 OSC 동아리 회원 여러분들과 두루물 회원 여러분들께 이 자리를 빌어서 감사의 말씀을 올립니다. 그리고 한, 한라, 허르미 등 좋은 한글라이브러리를 만들어 공개해주신 분들께도 감사드립니다.

혹시 '어 저것은 내가 짠 프로그램인데?' 하는 분이 있으면 언제라도 연락을 주기 바랍니다. 프로그램을 처음으로 만들어 올리신 분의 이름을 소스파일 앞에 넣어서 밝히도록 하겠습니다.

다시 한 번 정보의 공유를 위하여 프로그램의 소스를 공개해주신 많은 분들에게 감사드리면서 이 책을 통해 선보이는 프로그램이 좀더 좋은 프로그램으로 다듬어져 다른 분들에게 공개되기를 바랍니다.

이번 마당에서는 그 동안 배운 기초적인 문법을 검토할 수 있는 프로그램을 선보입니다. 자세한 설명을 달지 않겠으니 여러분 스스로 꼼꼼하게 프로그램을 살펴보면서 왜 이렇게 되는지 살펴보고 넘어가기 바랍니다.

그리고 소스 파일 중에서 그래픽 관련 예제는 터보C에서만 지원되는 예제임을 잊지 말기 바랍니다.

52.1. 반올림 함수 만들기

C언어 이야기 책 소개
각종 세금이나 요금을 계산할 때는 반올림을 해서 계산할 때가 많습니다. 이번 프로그램은 반올림을 하는 함수입니다. 프로그램은 간단하지만 여러 가지 다양한 함수와 연산자를 사용하고 있습니다. 삼항 연산자를 사용하는 방법과 ceil, floor, pow10 등의 함수를 어떻게 사용하는지 살펴볼 만합니다. 또 결과값에 바로 함수를 대입하여 프로그램을 간결하게 만든 것도 눈여겨볼 만합니다.

#include <stdio.h> #include <math.h> double banollim(double x, double y); /* 앞의 수는 반올림할 숫자, 뒤의 수는 반올림 원하는 십단위자리수 */ /* 0.1자리의 반올림이면 뒤숫자가 -1,일단위반올림이면 0,백단위면 2가 됨 */ double banollim(double x, double y) { x=x/pow10(y+1); return (((x>0) ? floor(x+.5):ceil(x-.5))*pow10(y+1)); } main() { double n=456.456; printf(" %f7 \n", banollim(n, 1)); printf(" %f7 \n", banollim(n, 0)); printf(" %f7 \n", banollim(n, -1)); printf(" %f7 \n", banollim(n, -2)); printf(" %f7 \n", banollim(n, -3)); }

52.2. 커서를 안 보이게


커서의 모양을 바꾸는 함수입니다. 프로그램을 짜다 보면 어떤 경우에는 커서가 안보여야 하고 어떤 경우에는 다시 나타나야 합니다. 아래의 프로그램은 인터럽트를 이용하여 커서 크기를 바꾸는 함수로 프로그램을 짤 때 요긴하게 사용할 수 있는 함수입니다.

#include <stdlib.h> #include <dos.h> void cur_off(int yy1,int yy2); main() { cur_off(0,0); /* 커서가 나타남 */ getch(); cur_off(255,255); /* 커서가 안 보임 */ getch(); cur_off(0,0); /* 다시 커서가 나타남 */ getch(); } /* 변수 값에 따라 변하는 커서 */ /* 두 값이 모두 255,255 면 사라짐. */ void cur_off(int yy1,int yy2) { union REGS r; r.h.ah=1; r.h.ch=yy1; r.h.cl=yy2; int86(0x10,&r,&r); }

52.3. 도레미파솔라시도 만드는 함수


이번에는 소리를 만드는 함수입니다. 컴퓨터의 내장스피커를 이용하여 음악을 만들 때 사용합니다. 일정 높이의 주파수를 일정 시간 동안 소리냄으로써 간단한 음계를 표현할 수 있습니다. 아래의 프로그램을 실행시키면 도레미파솔라시도를 냅니다. 주파수 높이(herz)를 조정하면 음계를 더욱 다양하게 만들 수 있고, 길이를 조정하여 박자를 맞출 수 있습니다. 비록 내장스피커지만 사용할 만 합니다.

내장스피커를 이용한 음악은 프로그램이 에러 났을 때나 프로그램의 실행이 완료되었을 때 알려주는 소리로 사용할 수도 있습니다. 활용범위가 넓으니 내장스피커를 이용하여 음악을 들려줄 수 있는 아래의 프로그램을 기억해두시기 바랍니다.

주의할 점은 delay() 함수에 대한 부분입니다. 이 함수의 경우 벌레가 있어 시간을 제대로 지연시키지 못합니다. 원래는 컴퓨터의 시간을 이용해 1000의 값을 주면 1초 동안 정지시켜야 하는데, 클럭수에 연동되는 문제가 있습니다. 그래서 과거의 구형 컴퓨터인 XT나 286에서는 그럭저럭 1000의 값을 주면 1초 동안 작업을 지연시켜 주지만 펜티엄급의 컴퓨터에서는 그야말로 순식간에 1000의 디레이 시간이 지나갑니다. 현재 제가 사용 중인 셀러론 600MHz 정도의 컴퓨터라면 20000 정도는 주어야 시간을 두고 도레미파솔라시도가 연주됩니다. delay() 함수의 값으로 1000을 주면 시스템의 속도에 따라 지연 시간에 차이 난다는 사실 잊지 말고 기억하기 바랍니다.

/* doremi.c 도레미파솔라시도 함수 */ #include <stdlib.h> void beep(unsigned herz, unsigned milisec); main() { beep(262, 20000); /* 도 음계 */ beep(294, 20000); /* 레 음계 */ beep(330, 20000); /* 미 음계 */ beep(349, 20000); /* 파 음계 */ beep(392, 20000); /* 솔 음계 */ beep(440, 20000); /* 라 음계 */ beep(494, 20000); /* 시 음계 */ beep(524, 20000); /* 도 음계 */ } /* 소리 만드는 함수. 이것을 이용해서 음악을 만들 수 있음. */ /* herz 는 주파수 높이고, milisec는 소리가 유지되는 시간임. */ void beep(unsigned herz, unsigned milisec) { sound(herz); delay(milisec); nosound(); }

52.4. shell.c = 가장 단순한 도스나들이 기능


가장 단순하게 도스 나들이 기능을 구현하는 프로그램입니다. command.com을 사용했으므로 언제나 도스나들이가 된다는 보장이 없습니다. 만약 command.com 파일이 path로 걸려있지 않거나 히든파일로 숨겨두었다면 낭패를 볼 수 있기 때문입니다. 이럴 경우에는 도스나들이가 실패합니다. 그러나 보통은 아래와 같은 방법을 통해서 도스나들이프로그램을 사용합니다. command.com을 사용하므로 exit라고 치면 다시 원래의 프로그램을 돌아오게 됩니다.

먼저 도스의 prompt 명령을 이용하여 프롬프트 모양을 다음과 같이 바꿉니다.

Type EXIT to return to DOS mode.
C:\TC>_

프롬프트 기호인 $_ 기호는 캐리지리턴과 라인피드를 하라는 특수명령어입니다. 그리고 $p는 prompt(경로)를 표시하라는 기호이고, $g는 > 기호를 표시하라는 명령입니다. 그래서 'Type EXIT to return to DOS mode.'라는 글을 보여준 뒤에 다음줄 첫번째 칸으로 경로명을 표시해주는 것입니다. 아마도 많은 분들이 도스나들이를 했을 때 'Type EXIT to return to program'이나 'Type EXIT to return to hwp'라는 보셨을 겁니다. 아래의 shell.c 프로그램을 이용하면 이런 도스나들이 기능을 어렵지 않게 구현할 수 있습니다. 만약 여러분들이 원하는 문구가 있다면 그 문구를 대신 사용해도 됩니다.

shell.c의 내용

#include <stdlib.h> void dosshell() { char buf[100]; strcpy(buf,getenv("PROMPT")); putenv("PROMPT=Type EXIT to return to DOS mode. $_$P$G"); /* 이 부분이 바로 'Type EXIT to return to DOS mode.'라는 글이 표시된 뒤에 디스크의 경로명을 표시하는 곳입니다. */ system("COMMAND.COM"); /* 이렇게 해서 도스나들이 기능이 실행됩니다. */ putenv(buf); } main() { dosshell(); }

52.5. dosver.c = 도스 버전을 알고 싶을 때


printf() 함수만을 사용한 아주 간단한 프로그램이지만 도스버전을 알 수 있습니다. 도스버전을 알아내기 위하여 _osmajor과 같은 터보C의 예약변수이름을 사용하고 있습니다. 예약된 이름을 어떻게 사용하는지 참고가 되실 것으로 생각합니다.

아래 프로그램에서 _osmajor은 첫째자리수의 버전을 구해주고, _osminor은 소수 첫번째 자리수의 버전을 구해줍니다. 따라서 도스 6.0을 사용중이라면 'MS-DOS Version 6.0'이라고 표시가 될 겁니다. 만약 printf("MS-DOS Version %d.%d", _osmajor, _osminor); 문의 변수를 printf("MS-DOS Version %d.%d", _osmajor, _osmajor); 라고 고치면 둘 다 첫번째 자리수를 표시하므로 6.6이 나올 것이고, 두 개의 변수를 모두 _osminor이라고 한다면 0.0이 도스버전으로 출력됩니다. 그렇기 때문에 '%d.%d'와 같은 형식으로 도스버전을 표시하도록 한 것입니다.

dosver.c의 내용

#include <stdio.h> #include <dos.h> void main(void) { printf("MS-DOS Version %d.%d", _osmajor, _osminor); }

52.6. copystr.c = 입력받은 문자를 다시 보여줌


이 프로그램은 main()함수에서 매개변수를 사용하는 보기입니다. argc와 argv를 어떻게 사용하는지 살펴보기 바랍니다. 도스에서 copystr이라고만 치면 아무런 일도 안하지만 'copystr kim joongtae manse!!'라고 입력하면 바로 다음 줄에 'kim joongtae manse!!'라고 출력되는 것을 볼 수 있습니다. 간단한 프로그램이지만 입력받은 자료를 다시 화면으로 보여주는 여러 가지 프로그램에서 응용할 수 있습니다. 단 큰따옴표(") 등의 특수기호는 출력되지 않으니 이 부분을 보완해야 합니다.

문법설명에서 말씀드린대로 main()함수는 두 가지 인수를 가집니다. 첫번째 인자는 명령어에 사용하는 문자열의 갯수를 정수값으로 돌려줍니다. 즉 copystr이라고만 치면 1을 돌려주고 'copystr kim joongtae manse'라고 치면 4를 돌려줍니다. 보통 관례상 첫번째 인자의 변수이름을 argc라고 하는데 argc는 argument count의 약자입니다. 이때 argc가 돌려주는 인자의 갯수는 공백을 기준으로 합니다. 즉 첫 단어 다음에 공백이 있고 다음에 또 글씨가 있으면 그곳부터 두번째 인자로 파악합니다.

두번째 인자는 문자열 포인터 배열입니다. 그러니까 명령행의 각 문자내용을 기억하는 것입니다. 따라서 copystr kim joongtae manse'는 argc로는 4를 돌려주고 argv[]에는 다음과 같이 저장되는 것입니다.

argv[0] = copystr
argv[1] = kim
argv[2] = joongtae
argv[3] = manse

이때 주의할 점은 저장될 때는 공백(빈칸)을 제거하고 저장된다는 사실입니다. 따라서 아래의 프로그램에서 %s 다음의 빈칸을 넣어주어야 argv가 연속적으로 출력될 때 한 칸씩 빈 칸이 들어갑니다. 두 문장은 다음과 같은 차이가 있습니다.

printf("%s ",argv[c]); /* 'kim joongtae manse'라고 출력됩니다. */
printf("%s",argv[c]); /* 'kimjoongtaemanse'라고 출력됩니다. */

copystr.c의 내용

#include <stdio.h> main(int argc, char *argv[]) { int c; if (argc < 2) /* 명령어만 치고 매개변수를 입력하지 않으면 */ { /* 사용법을 보여주라는 글을 보여줍니다 */ printf("\n Copy String v 1.0 "); printf("\n Usage : COPYSTR [String] \n"); return 0; } for (c = 1;c < argc;c++) printf("%s ",argv[c]); /* argv의 배열 요소를 하나씩 화면으로 보여줍니다 */ }

52.7. typefile.c = 간단한 문서파일 내용을 보여줍니다.


한 줄의 문장을 출력해주는 프로그램을 이용하면 파일 전체를 화면으로 보여주는 프로그램도 손쉽게 만들 수 있습니다. 이번에는 매개변수로 파일을 입력할 경우 파일의 내용을 보여주는 프로그램입니다. 예를 들어서 'typefile typefile.c'라고 하면 typefile.c의 내용을 화면으로 보여줍니다. 그리고 typefile.c 파일이 없을 경우에는 파일이 없다는 말을 보여줍니다.

이 프로그램 역시 도스의 type 명령어와 같은 기능을 수행합니다. 그러나 에러처리 루틴이 없기 때문에 이진파일이나 중간에 이상한 코드, 예를 들면 파일의 끝을 나타내는 특수코드 등이 있는 파일은 다 보여주지 못하고 중간에 멈춥니다. 그렇기 때문에 간단한 문서파일이나 제대로 보여줄 수 있는 프로그램입니다.

좀더 복잡하고 제대로 된 프로그램은 통신망에 올려놓은 listfile.c라는 소스를 참고하시면 됩니다.

typefile.c의 내용

#include <stdio.h> main(int argc,char *argv[]) { FILE *in; /* 파일을 사용하기 위한 파일 포인터를 선언합니다. */ int ch; if (argc==1) { printf("\n TYPEFILE v 1.0 \n"); printf(" Usage: TYPEFILE [filename] \n"); /* 매개변수가 하나도 없을 때는 사용법을 보여주라는 명령문 */ } else { /* 매개변수가 있다면 */ if ((in=fopen(argv[1],"r")) != NULL) /* 첫번째 매개변수를 파일이름으로 알고*/ { /* 매개변수의 파일을 엽니다 */ while ((ch=getc(in)) != EOF) /* 파일의 끝을 만나기전까지 반복 */ putc(ch,stdout); fclose(in); /* 파일을 닫습니다. */ } else printf("\n File not found! \n"); /* 파일열기에 실패하면 */ } /* 파일이 없다는 말을 출력합니다. */ }

52.8. list.c = 파일의 내용을 보여줍니다


이 프로그램은 도스의 type 명령어와 같은 기능을 수행합니다. 파일의 내용을 화면으로 보여주면서 스크롤해주는 프로그램입니다.

list.c의 내용

/* LARGE모델로 컴파일 할것! */ #if !defined(__LARGE__) #error Compile with the LARGE memory model! #endif #include <dos.h> #include <alloc.h> #include <conio.h> #include <ctype.h> #include <errno.h> #include <fcntl.h> #include <process.h> #include <stdarg.h> #include <stdio.h> #include <string.h> #define MAX_BUF_SIZE 65534 /* 파일을 읽기 위한 버퍼 */ typedef unsigned char byte; /* 사용자 정의 자료형 */ typedef unsigned int word; typedef enum {false=0, true=1} bool; byte far *buffer; /* 버퍼 포인터 */ long getFileSize(FILE *stream) /* 파일 끝표시(01Ah)를 무시하고 */ { /* 모든 파일의 내용을 읽기 위해 */ long fend; /* 파일의 실제 크기를 계산한다. */ fseek(stream, 0L, SEEK_END); fend = ftell(stream); fseek(stream, 0L, SEEK_SET); return (long)fend; } /* 사용자의 형식에 맞는 화면 출력을 위한 함수 */ /* 공백/CR/LF 등에 대해 적절히 행동한다. */ void xputch(int c) { if (c < 32) { if (c == '\t') cputs(" "); else if (c == '\r' || c == '\n') putch(c); else putch(' '); } else putch(c); } void xcputs(const char *s) { while (*s) xputch(*s++); } void xcprintf(char *fmt, ...) { char buf[256]; va_list vp; va_start(vp, fmt); vsprintf(buf, fmt, vp); va_end(vp); xcputs(buf); } /* +----------------------------------------------------+ */ /* | 면을 위로 스크롤하는 함수 | */ /* | 바이오스 인터럽트 10h의 부함수 6번을 사용한다. | */ /* | l = 스크롤할 화면의 최좌측 | */ /* | t = " 최상단 | */ /* | r = " 최우측 | */ /* | b = " 최하단 | */ /* | attr = " 색상값((배경색<<4)+문자색) | */ /* | lines = " 행수 | */ /* +----------------------------------------------------+ */ void scrollup(byte l, byte t, byte r, byte b, byte attr, byte lines) { union REGS reg; reg.h.ah = 6; reg.h.al = lines; reg.h.bh = attr; reg.h.ch = t; reg.h.cl = l; reg.h.dh = b; reg.h.dl = r; int86(0x10, &reg, &reg); } /* +------------------------------------------------+ */ /* | 파일의 내용을 읽는 함수 | */ /* | stream = 파일포인터 | */ /* | fsize = 파일의 실제 크기 | */ /* | spos = 파일의 현재 위치 | */ /* | rsize = 읽을 바이트 수 | */ /* | buf = 읽은 내용을 저장할 버퍼 | */ /* | more = 파일을 다읽지 못했다면 1, 아니면 0 | */ /* +------------------------------------------------+ */ word readFile(FILE *stream, long fsize, long *spos, word rsize, byte far *buf, bool *more) { long tsize = 0L; int flag; if (fseek(stream, *spos, SEEK_SET) != 0) { *more = false; return 0; } if ((*spos)+rsize <= fsize) { tsize = rsize; *more = true; } else { tsize = rsize-(((*spos)+rsize)-fsize); *more = false; } fread((byte far *)buf, 1, (size_t)tsize, stream); *spos = ftell(stream); return tsize; } int main(int argc, char *argv[]) { FILE *stream; bool more = true, continueflag = true; long pos = 0L, fsize; word rsize = MAX_BUF_SIZE, tsize = 0, i, j; if(argc < 2) { xcputs("\r\nCommand-Line Parameter Missing!\r\n"); xcputs("USAGE: LIST <filename.ext>\r\n"); return(1); } stream = fopen(argv[1], "rb"); if (stream == NULL) { cprintf("\a\r\nFile not found - '%s'\r\n", argv[1]); return EBADF; } /* 파일을 읽기 위한 버퍼의 확보 */ buffer = (byte far *)farmalloc(MAX_BUF_SIZE); if (buffer == NULL) { fclose(stream); xcputs("\a\r\nOut of memory!\r\n"); return ENOMEM; } fsize = getFileSize(stream); tsize = readFile(stream, fsize, &pos, rsize, buffer, &more); if (tsize == 0) return EZERO; while (continueflag) { for (i = 0; i < tsize; i++) { xputch(*(buffer+i)); } if (more) { tsize = readFile(stream, fsize, &pos, rsize, buffer, &more); if (tsize == 0) { continueflag = false; break; } } else { continueflag = false; break; } } fclose(stream); farfree((byte far*)buffer); return EZERO; }

52.9. copyfile.c = 텍스트파일을 복사하는 파일복사 프로그램


파일을 보여줄 수 있다면 복사도 어렵지 않습니다. 파일의 내용을 con(화면)으로 출력하던 것을 파일로 출력하도록 방향만 바꾸면 되기 때문입니다. 이번 프로그램은 문서파일을 복사해주는 프로그램입니다. 도스의 copy 명령어와 큰 차이가 없으므로 도스명령어와 비슷한 프로그램을 만들 때 도움이 될 겁니다.

파일내용을 보여주는 프로그램과 마찬가지로 도스상에서 매개변수를 이용하여 파일을 입력받았으며, 매개변수의 수가 맞지 않으면 사용법을 보여줍니다.

copyfile.c의 내용

#include <stdio.h> main(int num, unsigned char *FI[]) { FILE *source, *target; unsigned char imsi; /* 파일복사 때 사용할 임시 버퍼) if (num!=3) {printf(" Copyfile v 1.0 \n"); printf(" Usage: copyfile <source filename> <target filename> \n"); exit(1); } /* 매개변수가 3개가 아니면 사용법을 보여줍니다 */ source=fopen(FI[1],"r"); if (source=='\0') {printf("File not found! \n"); /* 파일을 찾지 못하면 */ exit(1); } target=fopen(FI[2],"w"); /* 복사할 파일을 새로 만듦 */ while(!feof(source)) { imsi=fgetc(source); fputc(imsi,target); } fcloseall(); printf(" File copied ! \n"); }

52.10. att.c = 파일의 속성을 바꿉니다




파일의 속성을 바꾸는 프로그램입니다. main()함수에서 어떻게 인수를 받아들이고 처리하는가와 파일을 열고 닫는 과정을 이해할 수 프로그램입니다. 인터럽트를 이용하는 기술이 나오는데 이 부분은 이해하기 어려울테니 어떻게 사용하는지만 참고하시기 바랍니다.

att.c의 내용

#include <dos.h> #include <stdio.h> #include <dir.h> #include <string.h> int attribute(char *name,char attrib) { union REGS r; struct SREGS s; r.h.ah = 0x43; r.h.al = 0x01; r.x.cx = attrib; r.x.dx = FP_OFF(name); s.ds = FP_SEG(name); int86x(0x21,&r,&r,&s); return(r.x.cflag); } void file_serch(char *name) { struct ffblk ffblk; static char attr[5]={'.','.','.','.'}; findfirst(name,&ffblk,0xff); if(ffblk.ff_attrib & FA_HIDDEN) attr[0]='H'; if(ffblk.ff_attrib & FA_ARCH ) attr[1]='A'; if(ffblk.ff_attrib & FA_RDONLY) attr[2]='R'; if(ffblk.ff_attrib & FA_SYSTEM) attr[3]='S'; attr[4]=NULL; printf("Filename = %-13s attribute = %-4s",ffblk.ff_name,attr); } void main(int argc,char *argv[]) { int attrib,chick,error=0; int hidden=0,archive=0,read=0,system=0; if(argc<3) { puts(" ATT v 1.0 "); puts(" ATT [filename] [attribute] [attribute] ..."); puts(" H = Hidden , A = Archive , R = Read only , S = system"); puts(" Usage: ATT test.exe h a"); } if(argc>2){ for(chick=2;chick<=argc;chick++){ if(strcmpi(argv[chick],"h")==0) hidden = FA_HIDDEN; if(strcmpi(argv[chick],"a")==0) archive = FA_ARCH; if(strcmpi(argv[chick],"r")==0) read = FA_RDONLY; if(strcmpi(argv[chick],"s")==0) system = FA_SYSTEM; } attrib=hidden+archive+read+system; error=attribute(argv[1],attrib); if(error!=0) puts("Error !"); else file_serch(argv[1]); } }

52.11. ff.c = 파일을 찾아줍니다


이번 프로그램은 도스의 파일을 찾아주는 프로그램입니다. 눈여겨봐야할 부분은 재귀호출 기능입니다. 재귀호출이란 어떤 함수가 다시 자신을 호출하는 행위를 말합니다. 재귀호출은 잘못 사용하면 프로그램의 폭주를 가져오기 때문에 시스템을 먹통으로 만들 수 있는 위험이 있습니다. 그렇기 때문에 초보자들은 재귀호출을 사용하는 일을 삼가야 합니다. 그러나 프랙탈 그래픽프로그램을 사용하거나 파일을 찾는 프로그램 등에서는 재귀호출을 자주 사용합니다. 파일을 찾는 프로그램은 서브디렉토리를 검색하다가 다시 위로 올라가서 나머지 서브디렉토리를 뒤지는 작업을 반복해야 하기 때문에 재귀호출기능을 사용하고 있습니다.

ff.c를 컴파일해서 만든 ff.exe는 원하는 파일을 디스크 전체를 뒤져서 찾아줍니다. 물론 *.bat와 같이 두루문자(wildcard)를 사용해서 찾을 수도 있기 때문에 매우 유용합니다.

ff.c의 내용

#include <stdio.h> #include <dos.h> #include <dir.h> #define ALL_ATTRIB FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_LABEL|FA_DIREC|FA_ARCH void findfile(char *mask) { char path[80]; struct ffblk info; if (findfirst(mask, &info, ALL_ATTRIB) == 0) { do { getcwd(path, 80); printf("\n%s%s%s", path, strlen(path) == 3 ? "" : "\\", info.ff_name); /* 루트디렉토리는 '\' 안찍어야 하므로 */ } while (findnext(&info) == 0); } if (findfirst("*", &info, FA_DIREC) == 0) { do { if (info.ff_name[0] == '.' || info.ff_attrib != FA_DIREC) continue; /* 디렉토리 아닌것과 .와 ..를 제거 */ chdir(info.ff_name); /* 아래 디렉토리로 찾아 들어감 */ findfile(mask); /* 이 부분에서 다시 재귀호출을 함 */ chdir(".."); /* 부모디렉토리로 다시 돌아옴 */ } while (findnext(&info) == 0); } } int main(int argc, char *argv[]) { char curpath[30]; if (argc < 2) { printf("\n FileFind v 1.0 "); printf("\n Usage : FF [filename] \n"); return 0; } getcwd(curpath, 30); chdir("\\"); findfile(argv[1]); chdir(curpath); return 0; }

52.12. calendar.c = 만년 달력


이 프로그램은 우리가 사용하는 달력을 만들어주는 프로그램입니다. 간단하지만 공휴일과 일요일을 빨간 색으로 표시해주는 기능까지 들어 있는 만년달력입니다.

calendar.c의 내용

/* ketel ID=sms999님의 프로그램입니다 */ #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <dos.h> #define UP 72 #define DOWN 80 #define LEFT 75 #define RIGHT 77 #define ESC 27 int days[] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; int HGC; int j_year,j_mon; int c_year = 0,c_mon = 0; struct date today; /* ------ 텍스트 모드에서 흑백인지 칼라인지 판별한다. -------*/ int videomode() { union REGS r; r.h.ah=15; return (int86 (16,&r,&r) &0xff); } /*------- 색깔을 (칼라,흑백에) 따라 지정한다. ---------*/ void color(int col,int ba,int color,int back) { if (HGC==7) textattr(col|ba<<4); else textattr(color|back<<4); } /* ------------- 커서를 나타나게 한다. ---------------*/ void cur_open() { union REGS r; r.h.ah = 1; r.h.cl = 0x0d; r.h.ch = 0x0c; int86(0x10,&r,&r); } /* ------------- 커서를 사라지게 한다. ---------------*/ void cur_close() { union REGS r; r.h.ah = 1; r.h.ch = 0x20; r.h.cl = 0x0c; int86(0x10,&r,&r); } /*--------- 양력의 공휴일을 일정색으로 칠한다.--------*/ void cal_date(int j) { if (j_mon == 1 && j == 1) color(15,0,12,5); if (j_mon == 1 && j == 2) color(15,0,12,5); if (j_mon == 3 && j == 1) color(15,0,12,5); if (j_mon == 4 && j == 5) color(15,0,12,5); if (j_mon == 5 && j == 5) color(15,0,12,5); if (j_mon == 6 && j == 6) color(15,0,12,5); if (j_mon == 7 && j == 17) color(15,0,12,5); if (j_mon == 8 && j == 15) color(15,0,12,5); if (j_mon == 10 && j == 3) color(15,0,12,5); if (j_mon == 12 && j == 25) color(15,0,12,5); } /*---- 년의 계산과 (년,월)증가,감소 판단 한다. -------*/ void cal(int y_k,int m_k) { int count = 1; int i,last,year,clr; long tot_nalsu; div_t aa,bb,cc; getdate(&today); if (m_k==0) c_mon = today.da_mon; if (j_year != 3000) if (y_k == 1) c_year++; if (j_year != 1000) if (y_k == 2) c_year--; if (j_year == 3000 && c_mon == 12) { } else if (m_k == 1) c_mon++; if (j_year == 1000 && c_mon == 1) { } else if (m_k == 2) c_mon--; j_mon=c_mon; if (j_mon==13) { c_year++; j_mon = 1; c_mon = 1; } if (j_mon==0) { c_year--; j_mon = 12; c_mon = 12; } j_year=today.da_year + c_year; color(7,0,14,5); gotoxy(2,1); cprintf("%c %4d YEAR %2d MON %c",6,j_year,j_mon,6); color(7,0,10,5); if (((j_year % 4 == 0) && (j_year % 100 != 0))|| (j_year % 400 == 0)) days[2] = 29; year=j_year - 1; aa=div(year,4); bb=div(year,100); cc=div(year,400); tot_nalsu = year * (long)365 + aa.quot - bb.quot + cc.quot; for(i=1;i<j_mon;i++) tot_nalsu = tot_nalsu + days[i]; tot_nalsu=tot_nalsu+1; last = tot_nalsu % 7; color(7,0,15,5); for (clr=7;clr<=8;clr++) { gotoxy(2,clr); cprintf(" "); } gotoxy(1,3); for (i=1;i<=last;i++) cprintf(" "); for (i=1;i<=days[j_mon];i++) { if (((i+last) % 7) == 1) color(15,0,12,5); cal_date(i); if (((i+last) % 7) == 0) color(9,0,11,5); if (i==today.da_day) { color(15,0,15,5);cprintf(" "); color(0,7,15,6);cprintf("%2d",i); } else cprintf("%3d",i); if (((i+last) % 7) == 0) { count++; gotoxy(1,2+count); } color(7,0,15,5); } } /*-------------- 키 사용정의와 초기화 ----------------*/ main() { char ch=0; textattr(7); clrscr(); HGC = videomode(); color(15,0,10,5); window(1,1,22,8); clrscr(); cur_close(); gotoxy(2,2); cprintf("SU MO TU WE TH FR SA"); cal(0,0); while(ch!= ESC){ ch=getch(); if (ch== 0) { switch(ch=getch()) { case RIGHT : cal(4,1); break; case LEFT : cal(4,2); break; case UP : cal(1,4); break; case DOWN : cal(2,4); break; } } } textattr(7); cur_open(); }

52.13. cpuspeed.c = CPU 속도를 측정


CPU의 속도를 측정해서 보여주는 프로그램입니다. 하드웨어의 바이오스에서 자료를 꺼내오는 방법을 보여주는 보기입니다.

cpuspeed.c의 내용

#include <math.h> #include <stdio.h> #include <dos.h> #include <conio.h> #define GetBiosTime() *((unsigned long far *)0x0000046CL) /* 바이오스 현재 시간을 읽음 */ int CheckCpuTime(); /* CPU 시간을 체크한다 return 값 : 뒤에 2자리는 소숫점 2자리라고 생각한다. 그래서 return값에 /100을 한값이 .2fMHz이다. */ int CheckCpuTime() { unsigned checktesttime = 2; unsigned long checkstarttime; unsigned long checkloop=0; int cpu_speed; checkstarttime = GetBiosTime(); while(checkstarttime == GetBiosTime()); /* 시간이 바뀔때 시작 */ checkstarttime = GetBiosTime() + 36; /* 36=약 2초(1초=18.24)*/ do { /* 메모리 입출력 검사 */ pokeb(0x0000,0x0000, peekb(0x0000,0x0000)); pokeb(0x0000,0x0000, peekb(0x0000,0x0000)); pokeb(0x0000,0x0000, peekb(0x0000,0x0000)); pokeb(0x0000,0x0000, peekb(0x0000,0x0000)); checktesttime = ((( checktesttime / (unsigned)2 ) * (unsigned)2 ) + 100 ); /*정수 연산 검사 */ checkloop++; checktesttime -= 100; /*정수 연산 검사 */ } while( checkstarttime > GetBiosTime() ); /*시간&조건 검사 */ cpu_speed = (int) (checkloop / 62); /* 원래는 6200(58MHz 기준)이나 정수계산을 위해 62으로 */ /* CPU 속도 구간별로 값을 약간씩 고쳐준다. 속도가 느릴수록 빼주는 비율이 커진다. */ if(cpu_speed < 900) cpu_speed -= cpu_speed/950; /* 9.5 예상값 */ else if(cpu_speed < 1200) cpu_speed -= cpu_speed/1020; /* 10MHz 기준 */ else if(cpu_speed < 1500) cpu_speed -= cpu_speed/1100; /* 예상값 */ else if(cpu_speed < 2000) cpu_speed -= cpu_speed/1290; /* 16MHz 기준 */ else if(cpu_speed < 3000) cpu_speed -= cpu_speed/1500; /* 예상값 */ else if(cpu_speed < 4500) cpu_speed -= cpu_speed/2000; /* 예상값 */ else cpu_speed -= cpu_speed/3200; return(cpu_speed); } main() { int data[] = { 60, 49, 45, 37, 31, 26, 19, 17, 11, 4, 1 }; unsigned char ID[][16] = { "Pentium(120MHz)", "Pentium(100MHz)", "Pentium( 90MHz)", "Pentium( 75MHz)", "Pentium( 60MHz)", " 486DX4(100MHz)", " 486DX4( 75MHz)", " 486DX2( 66MHz)", " 486DX2( 50MHz)", " 386DX ( 33MHz)", " AT 286( 12MHz)" }; int j, i, k, cpuspeed, barnum, one = 0; clrscr(); textcolor(WHITE); textbackground(MAGENTA); cputs("CPU Speed Checking Program ver1.0 1995/04/04 Hitel ID : jazonsim"); textbackground(BLACK); cpuspeed = CheckCpuTime(); barnum = (int)ceil(((float)cpuspeed * 11.7) / 4001); textcolor(WHITE); puts("\n Relativity Compare .. "); for(j = 0, k = 3; j < 11; j++, k++) { if(barnum > data[j] && one == 0) { one = 1; textcolor(LIGHTRED); gotoxy(1, k); cputs(" Your CPU is "); textcolor(WHITE); cputs("\xb3"); textcolor(LIGHTRED); for(i = 0; i < barnum; i++) cprintf("\xdc"); textcolor(WHITE); k++; } gotoxy(1, k); cprintf("%s \xb3", ID[j]); for(i = 0; i < data[j]; i++) cprintf("\xdc"); } printf("\n Relativity Numerical Value .. %d\n", cpuspeed); getch(); }

52.14. fall.c = 바이러스처럼 글씨가 떨어지는 램 상주 프로그램


이번 프로그램은 아주 독특한 프로그램입니다. 이 프로그램을 실행시키면 화면에 있던 글자들이 모두 후두둑 떨어져내립니다. 즉 화면 밑으로 쌓이는 프로그램입니다. 잘 모르는 사람은 바이러스로 착각하기 쉽습니다만, 그냥 재미 삼아 만들어볼만한 프로그램입니다. 인터럽트 사용법과 램상주프로그램의 기초를 제공할 수 있는 예제입니다.

fall.c의 내용

#include <dos.h> #include <conio.h> void interrupt newint(); static void interrupt (*oldint)(); void main(void) { int c; c=peekb(0,254); if (c==19) { disable(); printf("\nRemoved.\n\n"); enable(); pokeb(0,254,18); exit(0); } if (c!=18) { disable(); *oldint=getvect(8); setvect(8,newint); enable(); } printf("Falling.......................................\n"); printf("By dongper(Changkyu)-------------------------\n"); printf("If you want remove this program:Repress Fall.\n"); printf("**********************************************\n"); pokeb(0,254,19); keep(0,getpsp()); exit(0); } void interrupt newint() { if (peekb(0,254)!=18) { int i,m; for (i=3840;i>=0;i=i-2) { if (peekb(0xb800,i)!=32) { if (peekb(0xb800,i+160)==32) { m=peekb(0xb800,i+161); pokeb(0xb800,i+160,peekb(0xb800,i)); pokeb(0xb800,i+161,peekb(0xb800,i+1)); pokeb(0xb800,i,32); pokeb(0xb800,i+1,m); } } } } (*oldint)(); }




첫줄로(go top, go first line) 문화원첫화면으로(go dal site home) 강좌차림으로(go Chair) 사이트맵으로(go sitemap)




total chairpost