Last active
August 29, 2015 14:07
-
-
Save gongzhitaao/483abc94880080f5ad69 to your computer and use it in GitHub Desktop.
COMP 2710 Lab2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <algorithm> | |
#include <cstdio> | |
#include <cstring> | |
#include <ctime> | |
#include <iostream> | |
#include <fstream> | |
#include <set> | |
#include <sstream> | |
#include <string> | |
#include <vector> | |
#include <fcntl.h> | |
#include <sys/stat.h> | |
#include <unistd.h> | |
namespace lab2 { | |
struct entry { | |
time_t ts; | |
std::string sender; | |
std::string reciever; | |
std::string content; | |
}; | |
inline bool fexists(const char *fname) | |
{ | |
struct stat buffer; | |
return (stat (fname, &buffer) == 0); | |
} | |
bool lockfile(const char *fname, int &fd, bool wait = false) | |
{ | |
if (!fexists(fname)) { | |
fd = open(fname, O_CREAT, S_IRUSR | S_IWUSR); | |
close(fd); | |
} | |
if ((fd = open(fname, O_RDWR)) == -1) { | |
perror("open"); | |
exit(1); | |
} | |
// l_type, l_whence, l_start, l_len | |
flock wl = {F_WRLCK, SEEK_SET, 0, 0}; | |
if (wait) | |
return (-1 != fcntl(fd, F_SETLKW, &wl)); | |
return (-1 != fcntl(fd, F_SETLK, &wl)); | |
} | |
bool unlockfile(int fd) | |
{ | |
// assume fd is valid | |
flock ul = {F_UNLCK, SEEK_SET, 0, 0}; | |
return (-1 != fcntl(fd, F_SETLK, &ul)); | |
} | |
inline std::string grpfname(const std::string &grp) | |
{ | |
return "dat/Group/" + grp; | |
} | |
inline std::string usrfname(const std::string &usr) | |
{ | |
return "dat/User/" + usr; | |
} | |
void gethist(int fd, std::vector<entry> &hist, | |
const std::string &sender = "", | |
const std::string &reciever = "") | |
{ | |
int len = lseek(fd, 0, SEEK_END); | |
char *s = new char[len + 1]; | |
memset(s, 0, len + 1); | |
lseek(fd, 0, SEEK_SET); | |
read(fd, s, len); | |
for (int a = 2, b; a < len; a = b + 2) { | |
// extract timestamp: integer | |
for (b = a + 1; s[b] != ')'; ++b) | |
; | |
s[b] = 0; | |
time_t ts = atoi(s + a); | |
// extract sender | |
a = b + 4; | |
for (b = a; !('>' == s[b] && '|' == s[b+1]); ++b) | |
; | |
s[b] = 0; | |
std::string name = s + a; | |
// extract content | |
a = b + 2; | |
for (b = a; b < len - 1 && !('[' == s[b] && '(' == s[b+1]); | |
++b) | |
; | |
std::string content; | |
if (b == len - 1) | |
content = s + a; | |
else { | |
s[b] = 0; | |
content = s + a; | |
} | |
if (sender.empty()) | |
hist.push_back({ts, name, reciever, content}); | |
else | |
hist.push_back({ts, sender, name, content}); | |
} | |
delete s; | |
} | |
void reverse(int fd) | |
{ | |
std::vector<entry> hist; | |
gethist(fd, hist); | |
sort(hist.begin(), hist.end(), | |
[](const entry &a, const entry &b) { | |
return difftime(a.ts, b.ts) > 0; | |
}); | |
lseek(fd, 0, SEEK_SET); | |
for (auto it = hist.begin(); it != hist.end(); ++it) { | |
std::string tmp = | |
"[(" + std::to_string(it->ts) + ")]" + "|" + // timestamp | |
"<" + it->sender + ">" + "|" + // sender/reciever | |
it->content; // msg | |
write(fd, tmp.c_str(), tmp.size()); | |
fsync(fd); | |
} | |
} | |
void msgrw(int ffd, const std::string &from, | |
const std::string &to) | |
{ | |
/* read in the multiline message */ | |
printf("\nEnter message: "); | |
std::string msg, line; | |
while (1) { | |
getline(std::cin, line); | |
size_t sz = line.size(); | |
if (sz >= 2 && | |
line[sz-1] == '$' && | |
line[sz-2] == '$') { | |
msg += line.substr(0, sz - 2); | |
break; | |
} | |
msg += line + "\n"; | |
} | |
// get current timestamp | |
time_t ts; | |
time(&ts); | |
/* Now we proceed to send the message. */ | |
// senders | |
{ | |
// construct msg: format: [(timestamp)]|Reciver|msg | |
std::string tmp = | |
"[(" + std::to_string(ts) + ")]" + "|" + // timestamp | |
"<" + to.substr(to.find_last_of('/') + 1) + ">" + "|" + // receiver | |
msg; // msg | |
lseek(ffd, 0, SEEK_END); | |
write(ffd, tmp.c_str(), tmp.size()); | |
fsync(ffd); | |
reverse(ffd); | |
} | |
// receiver | |
{ | |
// construct msg: format: [(timestamp)]|Sender|msg | |
std::string tmp = | |
"[(" + std::to_string(ts) + ")]" + "|" + // timestamp | |
"<" + from + ">" + "|" + // sender | |
msg; // msg | |
int fd; | |
if (!lockfile(to.c_str(), fd, true)) { | |
perror("fcntl"); | |
exit(1); | |
} | |
lseek(fd, 0, SEEK_END); | |
write(fd, tmp.c_str(), tmp.size()); | |
fsync(fd); | |
reverse(fd); | |
if (!unlockfile(fd)) { | |
perror("fcntl"); | |
exit(1); | |
} | |
close(fd); | |
} | |
} | |
class DMS | |
{ | |
public: | |
static DMS & inst() { | |
static DMS dms; | |
return dms; | |
} | |
~DMS() { | |
if (ufd_ != -1) { | |
std::ofstream of((usrfname(user_) + ".txt").c_str()); | |
for (auto it = curgrp_.begin(); it != curgrp_.end(); ++it) | |
of << *it << std::endl; | |
of.close(); | |
if (!unlockfile(ufd_)) { | |
perror("fcntl"); | |
exit(1); | |
} | |
} | |
} | |
void run() { | |
printf("\n" | |
"============================================================\n" | |
"| Distributed Messaging System! |\n" | |
"============================================================\n\n"); | |
login(); | |
while (1) { | |
printf("\n" | |
"Broadcast (b), Multicast (m), Unicast(u), Wall (w), Home (h),\n" | |
"Create Group (g), Join Group (j), Quit (q)\n"); | |
printf("\nEnter option: "); | |
int opt = getchar(); | |
while (getchar() != '\n') ; | |
switch (opt) { | |
case 'b': | |
broadcast(); break; | |
case 'm': | |
multicast(); break; | |
case 'u': | |
unicast(); break; | |
case 'w': | |
wall(); break; | |
case 'h': | |
home(); break; | |
case 'g': | |
create(); break; | |
case 'j': | |
join(); break; | |
case 'q': | |
quit(); goto end; | |
default: | |
fprintf(stderr, "Unknown options\n"); | |
} | |
} | |
end: | |
; | |
} | |
protected: | |
void login() { | |
while (1) { | |
printf("Please enter user name: "); | |
// Get username from input | |
getline(std::cin, user_); | |
// Empty username is invalid | |
if (user_.empty()) continue; | |
/* We allow one instance per user. If you logged in somewhere | |
else, then the file `dat/User/username` will be locked. In | |
that case you will have to login as another user. | |
*/ | |
if (!lockfile(usrfname(user_ + ".out").c_str(), ufd_)) { | |
// perror("fcntl"); | |
fprintf(stderr, "You've logged in somewhere else.\n"); | |
continue; | |
} | |
break; | |
} | |
int fd; | |
// create `username` file | |
fd = open(usrfname(user_).c_str(), O_CREAT, S_IRUSR | S_IWUSR); | |
close(fd); | |
// create `username.txt` or read in from it if exists | |
const char *tmp = (usrfname(user_) + ".txt").c_str(); | |
if (!fexists(tmp)) { | |
fd = open(tmp, O_CREAT, S_IRUSR | S_IWUSR); | |
close(fd); | |
} else { | |
std::ifstream fin(tmp); | |
std::string grp; | |
while (fin >> grp) | |
curgrp_.insert(grp); | |
} | |
printf("\n" | |
"============================================================\n" | |
"| Welcome to Distributed Messaging System, %-12s|\n" | |
"============================================================\n", | |
(user_ + "!").c_str()); | |
} | |
void broadcast() { | |
msgrw(ufd_, user_, grpfname("All")); | |
} | |
void multicast() { | |
if (curgrp_.empty()) { | |
fprintf(stderr, "User is not in any group.\n"); | |
return; | |
} | |
printf("\nEnter the group name: "); | |
std::string to; | |
getline(std::cin, to); | |
if (!to.empty() && curgrp_.find(to) != curgrp_.end()) | |
msgrw(ufd_, user_, grpfname(to)); | |
else | |
fprintf(stderr, "User is not in %s\n", to.c_str()); | |
} | |
void unicast() { | |
printf("\nEnter the recipient user name: "); | |
std::string to; | |
while (to.empty()) | |
getline(std::cin, to); | |
if (!fexists(usrfname(to).c_str())) { | |
fprintf(stderr, "No user named %s\n", to.c_str()); | |
return; | |
} | |
msgrw(ufd_, user_, usrfname(to)); | |
} | |
void wall() { | |
printf("\n" | |
"============================================================\n" | |
"|%25s’s Wall Page |\n" | |
"============================================================\n\n", | |
user_.c_str()); | |
std::vector<entry> hist; | |
gethist(ufd_, hist, user_); | |
sort(hist.begin(), hist.end(), | |
[](const entry &a, const entry &b) { | |
return difftime(a.ts, b.ts) > 0; | |
}); | |
for (size_t i = 0; i < 2 && i < hist.size(); ++i) { | |
printf("%s(%s)>>\n%s\n", | |
user_.c_str(), hist[i].reciever.c_str(), | |
hist[i].content.c_str()); | |
} | |
if (hist.size() > 2) { | |
bool cont = false; | |
while (1) { | |
printf("\n More message? (yes/no): "); | |
std::string ans; | |
getline(std::cin, ans); | |
if (0 == ans.compare("yes")) { | |
cont = true; | |
break; | |
} else if (0 == ans.compare("no")) { | |
break; | |
} | |
} | |
if (cont) { | |
printf("\n"); | |
for (size_t i = 2; i < hist.size(); ++i) { | |
printf("%s(%s)>>\n%s\n\n", | |
user_.c_str(), hist[i].reciever.c_str(), | |
hist[i].content.c_str()); | |
} | |
} | |
} | |
printf("\n" | |
"============================================================\n" | |
"| End of %-35s|\n" | |
"============================================================\n\n", | |
(user_ + "'s Wall Page").c_str()); | |
} | |
void home() { | |
printf("\n" | |
"============================================================\n" | |
"|%25s’s Home Page |\n" | |
"============================================================\n\n", | |
user_.c_str()); | |
std::vector<std::string> flist = { | |
usrfname(user_), grpfname("All") | |
}; | |
for (auto it = curgrp_.begin(); it != curgrp_.end(); ++it) | |
flist.push_back(grpfname(*it)); | |
std::vector<entry> hist; | |
for (auto it = flist.begin(); it != flist.end(); ++it) { | |
int fd; | |
if (!lockfile(it->c_str(), fd, true)) { | |
perror("fcntl"); | |
exit(1); | |
} | |
gethist(fd, hist, "", it->substr(it->find_last_of('/') + 1)); | |
if (!unlockfile(fd)) { | |
perror("fcntl"); | |
exit(1); | |
} | |
close(fd); | |
} | |
sort(hist.begin(), hist.end(), | |
[](const entry &a, const entry &b) { | |
return difftime(a.ts, b.ts) > 0; | |
}); | |
for (size_t i = 0; i < 2 && i < hist.size(); ++i) { | |
if (!hist[i].reciever.compare(user_)) | |
printf("%s>>\n%s\n", | |
hist[i].sender.c_str(), | |
hist[i].content.c_str()); | |
else | |
printf("%s(%s)>>\n%s\n", | |
hist[i].sender.c_str(), hist[i].reciever.c_str(), | |
hist[i].content.c_str()); | |
} | |
if (hist.size() > 2) { | |
bool cont = false; | |
while (1) { | |
printf("\n More message? (yes/no): "); | |
std::string ans; | |
getline(std::cin, ans); | |
if (0 == ans.compare("yes")) { | |
cont = true; | |
break; | |
} else if (0 == ans.compare("no")) { | |
break; | |
} | |
} | |
if (cont) { | |
printf("\n"); | |
for (size_t i = 2; i < hist.size(); ++i) { | |
if (!hist[i].reciever.compare(user_)) | |
printf("%s>>\n%s\n", | |
user_.c_str(), | |
hist[i].content.c_str()); | |
else | |
printf("%s(%s)>>\n%s\n", | |
hist[i].sender.c_str(), hist[i].reciever.c_str(), | |
hist[i].content.c_str()); | |
} | |
} | |
} | |
printf("\n" | |
"============================================================\n" | |
"| End of %-35s|\n" | |
"============================================================\n\n", | |
(user_ + "'s Wall Page").c_str()); | |
} | |
void create() { | |
std::string gn; | |
while (1) { | |
printf("\nPlease enter the group name: "); | |
getline(std::cin, gn); | |
if (!gn.empty() && !fexists(grpfname(gn).c_str())) | |
break; | |
printf("Group name either empty or exists.\n"); | |
} | |
int fd = open(grpfname(gn).c_str(), O_CREAT, S_IRUSR | S_IWUSR); | |
close(fd); | |
} | |
void join() { | |
std::string gn; | |
while (1) { | |
printf("\nPlease enter the group name: "); | |
getline(std::cin, gn); | |
if (gn.empty()) { | |
printf("\nEmpty group name"); | |
continue; | |
} | |
if (!fexists(grpfname(gn).c_str())) { | |
fprintf(stderr, "\nNo group with name %s\n", gn.c_str()); | |
continue; | |
} | |
if (curgrp_.find(gn) != curgrp_.end()) { | |
fprintf(stderr, "\nYou are already in the group.\n"); | |
continue; | |
} | |
break; | |
} | |
curgrp_.emplace(gn); | |
} | |
void quit() { | |
printf("\n" | |
"============================================================\n" | |
"| Thank you for using the Distributed Messaging System |\n" | |
"============================================================\n\n"); | |
} | |
private: | |
DMS() | |
: user_("") | |
, ufd_(-1) { } | |
DMS(const DMS &); | |
void operator=(const DMS &); | |
std::string user_; | |
std::set<std::string> curgrp_; | |
int ufd_; | |
}; | |
} | |
int main() | |
{ | |
lab2::DMS &dms = lab2::DMS::inst(); | |
dms.run(); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CXX=g++ | |
EXE=main | |
FLG=-std=c++11 -O2 -Wall | |
all : $(EXE) | |
$(EXE) : main.cpp | |
$(CXX) $(FLG) -o $@ $< | |
clean : | |
rm -rf $(EXE) | |
dat-clean : | |
rm -rf dat/Group/* | |
rm -rf dat/User/* | |
.PHONY : all clean |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment