//*********************************************** // Simple Message Filer (Version0) // This is as C-like as I want to get // Main purpose of this program is to introduce the semester's // teaching project and to show some simple windowsNT file manipulation // FILE IO: // terminal input and output uses c++'s cin and cout operators // However for file I/O we use win32 file routines // even though c++ or c's file I/O may be more preferable because of // its portability. We are here to learn windowsNT not c++ // USER INTERFACE: // It's single character command driven with integer message numbers as appropriate. // Accepts both upper and lower case commands. // L: List the messages currently available // A: Adds a new message to the end of the list // D #: Deletes message # from list (actually just marks it for deletion) // S #: shows message # // I filename: inputs message list in filename // O [filename]: outputs message to filename(default is input filename) // H: prints commands (also ?) will work // Q: quit program // There can be only one blank between I or O and the filename ( pretty ugly huh?) // Messages are arbitrarily numbered from 1 to the last message // Messages consist of a topic( up to 50 characters) and text (up to 300) // Editing of existing messages is not supported in this version. // DATA STRUCTURE: // Messages are stored in a fixed length array - one message per array location // with a fixed space for the topic and text // AUTHOR: chris wild // DATE: July 27. 1997 //************************************************* #include #include // for file types const int SIZE_TOPIC = 50; // maximum size for topic field const int SIZE_TEXT = 300; // maximum size for message text field const int MAX_NUM_MSG = 10; // maximum number of messages // status flags for messages const int DELETED = 0; // denotes a deleted messages const int EMPTY = 1; // message slot is empty const int MSG_OK = 2; // messages is OK const int NO_SLOT_FOUND = -1; struct message { char topic[SIZE_TOPIC+1]; char text[SIZE_TEXT+1]; }; struct messages { message msg; int status; /// of this messages (used to find empty slots and mark deleted files) } msgList[MAX_NUM_MSG]; //************** prototypes of functions void listMessages(); void addMessage(); void deleteMessage(int msgNum); void showMessage(int msgNum); void inputMessages(char* filename); void outputMessages(char* filename); void main() { char filename[50] = ""; // file name as string char command; int messageNum; char helpMsg[] = "\nO(pen Message Folder) folder , L(ist messages), A(dd message)\n\ D(elete) #, S(how) #, C(lose Folder) [folder], H(elp)\n"; for(int i = 0; i < MAX_NUM_MSG; i++) { msgList[i].status = EMPTY; // mark slot empty } cout << " Welcome to the Message Filer (version 0):\n\n"; cout << helpMsg; do { cout << "% " ; // prompt cin >> command; switch (command) { case 'L': case 'l': listMessages(); break; case 'A': case 'a': cin.ignore(); addMessage(); break; case 'D': case 'd': cin >> messageNum; cin.ignore(); // newline deleteMessage(messageNum); break; case 'S': case 's': cin >> messageNum; cin.ignore(); // newline showMessage(messageNum); break; case 'O': case 'o': cin.ignore(); // blank after command cin.getline(filename,50); inputMessages(filename); break; case 'C': case 'c': if(cin.peek() != '\n') { cin.ignore(); // blank after command cin.getline(filename,50); } else cin.ignore(); outputMessages(filename); break; case 'H': case 'h': case'?': cin.ignore(); cout << helpMsg; break; case 'Q': case 'q': command = 'Q'; // make it upper case for ease of testing break; default: cout << "\nbad command: " << command << "; skipping rest of line\n"; do cin.get(command); while (command != '\n'); break; } } while (command != 'Q'); } //***************************************************************************** // Worker Functions: //***************************************************************************** //******** // listMessages: list messages currently available // OUTPUT FORMAT: // #: Topic // where # is an arbitrary messages number which = index in message array // Status: is 'D' for deleted or blank for not deleted void listMessages() { for(int i = 0; i < MAX_NUM_MSG; i++) { if (msgList[i].status == EMPTY) continue; // skip this slot if (msgList[i].status == DELETED) continue; // skip this slot cout << i+1 << ": " << msgList[i].msg.topic << endl; } } //******** // addMessage: adds a message to empty slot // topic must fit on one line // text is multi-line terminating in an empty line void addMessage() { int slot = NO_SLOT_FOUND; int i; //** find empty slot for(i = 0; i < MAX_NUM_MSG; i++) { if (msgList[i].status == EMPTY) { slot = i; break; } } if( slot == NO_SLOT_FOUND) { cout << " No more room available!\n"; return; } msgList[slot].status = MSG_OK; cout << "topic(up to " << SIZE_TOPIC << " characters)\n"; cin.getline(msgList[slot].msg.topic,SIZE_TOPIC); cout << "text (up to " << SIZE_TEXT << " characters), end text with empty line\n"; bool newLine = true; // used for detecting empty lines for(i = 0; i < SIZE_TEXT; i++) { if((msgList[slot].msg.text[i] = cin.get()) == '\n') { if(newLine) break; newLine = true; } else newLine = false; } msgList[slot].msg.text[i] = '\0'; // terminate string } //******** // deleteMessage: marks message for deletion void deleteMessage(int msgNum) { msgList[msgNum-1].status = DELETED; } //******** // showMessage: Shows message void showMessage(int msgNum) { cout << "Topic: " << msgList[msgNum-1].msg.topic << endl; cout << "Text: " << msgList[msgNum-1].msg.text << endl; } //******** // inputMessages: inputs messages from file void inputMessages(char* fileName) { HANDLE messageFile; // handle for message file DWORD nBytes; int i; for(i = 0; i < MAX_NUM_MSG; i++) { msgList[i].status = EMPTY; // mark slot empty } messageFile = CreateFile(fileName, // LPCTSTR -- pointer to character string GENERIC_READ, // DWORD -- accessMODE (constant defined in windows.h) 0, // DWORD -- share mode (we want exclusive access) 0, // LPSECURITY_ATTRIBUTES -- what can be inherited OPEN_EXISTING, // DWORD create -- file must already exist 0, // DWORD -- attributes 0); // HANDLE -- templatefile if(messageFile == INVALID_HANDLE_VALUE) { cerr << "Error: " << GetLastError() << " when opening file: " << fileName << endl; ExitProcess(1); } for(i = 0; i < MAX_NUM_MSG; i++) { if(!ReadFile(messageFile, // HANDLE to open file msgList[i].msg.topic, // pointer to input buffer SIZE_TOPIC+1, // size of input buffer &nBytes, // bytes actually read 0)) // overlapped structure (asynchronous I/O) break; if(nBytes == 0) break; // End of File if(!ReadFile(messageFile,msgList[i].msg.text,SIZE_TEXT+1,&nBytes,0)) break; if(nBytes == 0) break; msgList[i].status = MSG_OK; } CloseHandle(messageFile); } //******** // outputMessages: writes messages to file void outputMessages(char* fileName) { HANDLE messageFile; // handle for message file DWORD nBytes; messageFile = CreateFile(fileName,GENERIC_WRITE,0,0,CREATE_ALWAYS,0,0); if(messageFile == INVALID_HANDLE_VALUE) { cerr << "Error: " << GetLastError() << " when creating file: " << fileName << endl; ExitProcess(1); } for(int i = 0; i < MAX_NUM_MSG; i++) { if(msgList[i].status != MSG_OK) continue; if(!WriteFile(messageFile,msgList[i].msg.topic,SIZE_TOPIC+1,&nBytes,0)) break; if(nBytes == 0) break; if(!WriteFile(messageFile,msgList[i].msg.text,SIZE_TEXT+1,&nBytes,0)) break; if(nBytes == 0) break; } CloseHandle(messageFile); }