|
@@ -0,0 +1,706 @@
|
|
|
+#include <stdlib.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <curses.h>
|
|
|
+#include <ctype.h>
|
|
|
+#include <iomanip>
|
|
|
+#include <iostream>
|
|
|
+#include <sstream>
|
|
|
+#include <fstream>
|
|
|
+#include <vector>
|
|
|
+#include <memory>
|
|
|
+#include <cassert>
|
|
|
+#include <algorithm>
|
|
|
+#include <sys/stat.h>
|
|
|
+#include <sys/mman.h>
|
|
|
+#include <fcntl.h>
|
|
|
+#include <openssl/md5.h>
|
|
|
+
|
|
|
+std::string url_encode(const std::string &value) {
|
|
|
+ std::ostringstream escaped;
|
|
|
+ escaped.fill('0');
|
|
|
+ escaped << std::hex;
|
|
|
+
|
|
|
+ for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) {
|
|
|
+ std::string::value_type c = (*i);
|
|
|
+
|
|
|
+ // Keep alphanumeric and other accepted characters intact
|
|
|
+ if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
|
|
|
+ escaped << c;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Any other characters are percent-encoded
|
|
|
+ escaped << std::uppercase;
|
|
|
+ escaped << '%' << std::setw(2) << int((unsigned char) c);
|
|
|
+ escaped << std::nouppercase;
|
|
|
+ }
|
|
|
+
|
|
|
+ return escaped.str();
|
|
|
+}
|
|
|
+
|
|
|
+std::string exec(const char* cmd) {
|
|
|
+ char buffer[128];
|
|
|
+ std::string result = "";
|
|
|
+ std::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
|
|
|
+ if (!pipe) throw std::runtime_error("popen() failed!");
|
|
|
+ while (!feof(pipe.get())) {
|
|
|
+ if (fgets(buffer, 128, pipe.get()) != NULL)
|
|
|
+ result += buffer;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+enum MYFIELD_IDX{
|
|
|
+ MYFIELD_TITLE ,
|
|
|
+ MYFIELD_YEAR ,
|
|
|
+ MYFIELD_AUTHORS ,
|
|
|
+ MYFIELD_JOURNAL ,
|
|
|
+ MYFIELD_DOI ,
|
|
|
+ MYFIELD_NOTES ,
|
|
|
+ MYFIELD_PATH ,
|
|
|
+ MYFIELD_COUNT ,
|
|
|
+ MYFIELD_BLOCKSIZE,
|
|
|
+};
|
|
|
+static const char* MYFIELD_STR[]={
|
|
|
+ "TITLE" ,
|
|
|
+ "YEAR" ,
|
|
|
+ "AUTHORS" ,
|
|
|
+ "JOURNAL" ,
|
|
|
+ "DOI" ,
|
|
|
+ "NOTES" ,
|
|
|
+ "PATH" ,
|
|
|
+ "COUNT" ,
|
|
|
+ "BLOCKSIZE"
|
|
|
+};
|
|
|
+
|
|
|
+int file_md5(const char *fname, unsigned char result[MD5_DIGEST_LENGTH]){
|
|
|
+ int file_descript = open(fname, O_RDONLY);
|
|
|
+ if(file_descript < 0) return -1;
|
|
|
+
|
|
|
+ struct stat statbuf;
|
|
|
+ if(fstat(file_descript, &statbuf) < 0) return -1;
|
|
|
+ unsigned long file_size = statbuf.st_size;
|
|
|
+
|
|
|
+ char* file_buffer = (char*)mmap(0, file_size, PROT_READ, MAP_SHARED, file_descript, 0);
|
|
|
+
|
|
|
+ MD5((unsigned char*) file_buffer, file_size, result);
|
|
|
+
|
|
|
+ munmap(file_buffer, file_size);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+bool addpaper(WINDOW* win, std::string* data){
|
|
|
+ int ch='a';
|
|
|
+ int max_x,max_y;
|
|
|
+ getmaxyx(win, max_y, max_x);
|
|
|
+ clear();
|
|
|
+
|
|
|
+ mvprintw(1, 0, "ADD PAPER"); wclrtoeol(win);
|
|
|
+
|
|
|
+ std::string field_cap[MYFIELD_COUNT];
|
|
|
+ for(long i=0;i<MYFIELD_COUNT;i++){
|
|
|
+ field_cap[i]=MYFIELD_STR[i];
|
|
|
+ field_cap[i].append(":");
|
|
|
+ }
|
|
|
+
|
|
|
+ std::vector<int> field_coord(2*MYFIELD_COUNT);
|
|
|
+ std::string field_str[MYFIELD_COUNT];
|
|
|
+ for(size_t i=0;i<MYFIELD_COUNT;i++) {
|
|
|
+ field_coord[2*i+0]=5+4*i;
|
|
|
+ field_coord[2*i+1]=field_str[i].size();
|
|
|
+ }
|
|
|
+
|
|
|
+ int curr_field=0;
|
|
|
+ while(1) {
|
|
|
+
|
|
|
+ clear();
|
|
|
+ for(size_t i=0;i<MYFIELD_COUNT;i++) {
|
|
|
+ mvprintw(field_coord[2*i+0]-1, 0, field_cap[i].c_str() ); wclrtoeol(win);
|
|
|
+ mvprintw(field_coord[2*i+0] , 0, field_str[i].c_str() ); wclrtoeol(win);
|
|
|
+ }
|
|
|
+ move(field_coord[curr_field*2+0], field_coord[curr_field*2+1]);
|
|
|
+
|
|
|
+ ch=getch();
|
|
|
+ if(ch==10){ // Enter
|
|
|
+ break;
|
|
|
+ }else if(ch==27){ // Esc
|
|
|
+ for(size_t i=0;i<MYFIELD_COUNT;i++) field_str[i].clear();
|
|
|
+ break;
|
|
|
+ }else if(ch==259){ // Up
|
|
|
+ curr_field=(curr_field+MYFIELD_COUNT-1)%MYFIELD_COUNT;
|
|
|
+ }else if(ch==258){ // Down
|
|
|
+ curr_field=(curr_field+MYFIELD_COUNT+1)%MYFIELD_COUNT;
|
|
|
+ }else if(ch==263 || ch==330 || ch==127){ // Delete
|
|
|
+ if(field_str[curr_field].size() && field_coord[curr_field*2+1]){
|
|
|
+ field_str[curr_field].erase(field_coord[curr_field*2+1]-1,1);
|
|
|
+ field_coord[curr_field*2+1]--;
|
|
|
+ }
|
|
|
+ }else if(ch==260){ // Left
|
|
|
+ if(field_coord[curr_field*2+1]>0) field_coord[curr_field*2+1]--;
|
|
|
+ }else if(ch==261){ // Right
|
|
|
+ if(field_coord[curr_field*2+1]<field_str[curr_field].size()) field_coord[curr_field*2+1]++;
|
|
|
+ }else{ // default
|
|
|
+ addch(ch);
|
|
|
+ char cstr[10];
|
|
|
+ sprintf(cstr,"%c",ch);
|
|
|
+ field_str[curr_field].insert(field_coord[curr_field*2+1],cstr);
|
|
|
+ field_coord[curr_field*2+1]++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bool add=true;
|
|
|
+ for(size_t i=0;i<MYFIELD_COUNT;i++) data[i]=field_str[i];
|
|
|
+ for(size_t i=0;i<MYFIELD_COUNT;i++) if(!data[i].size()) add=false;
|
|
|
+
|
|
|
+ clear();
|
|
|
+ return add;
|
|
|
+}
|
|
|
+
|
|
|
+void displaypaper(WINDOW* win, const std::vector<std::string>& db, int curr_field){
|
|
|
+ if(db.size()/MYFIELD_BLOCKSIZE<=curr_field) return;
|
|
|
+
|
|
|
+ int ch='a';
|
|
|
+ int max_x,max_y;
|
|
|
+ getmaxyx(win, max_y, max_x);
|
|
|
+ clear();
|
|
|
+
|
|
|
+ const std::string& path=db[curr_field*MYFIELD_BLOCKSIZE+MYFIELD_PATH];
|
|
|
+ std::string title=db[curr_field*MYFIELD_BLOCKSIZE+MYFIELD_TITLE];
|
|
|
+ std::string year=db[curr_field*MYFIELD_BLOCKSIZE+MYFIELD_YEAR];
|
|
|
+ std::string doi =db[curr_field*MYFIELD_BLOCKSIZE+MYFIELD_DOI];
|
|
|
+
|
|
|
+ for(long i=0;i<MYFIELD_COUNT;i++){ // Display details
|
|
|
+ mvprintw(3*i, 0, MYFIELD_STR[i]);
|
|
|
+ mvprintw(3*i, 9, ":");
|
|
|
+ mvprintw(3*i, 12, db[curr_field*MYFIELD_BLOCKSIZE+i].c_str()); wclrtoeol(win);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(!doi.size()){ // Display bibtex
|
|
|
+ std::string cmd("curl -H \"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36\" -Ls \"http://search.crossref.org/dois?q=");
|
|
|
+ cmd.append("\"");
|
|
|
+ cmd.append(url_encode(title));
|
|
|
+ cmd.append("\"");
|
|
|
+ cmd.append("&year=");
|
|
|
+ cmd.append(year);
|
|
|
+ cmd.append("&rows=1&sort=score\" 2> /dev/null | grep \"\\\"doi\\\"\" | rev | cut -c 3- | rev | cut -c 13- ");
|
|
|
+ doi=exec(cmd.c_str());
|
|
|
+ if (doi.size()) doi.resize(doi.size()-1);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(doi.size()){ // Display bibtex
|
|
|
+ std::string bibtex;
|
|
|
+
|
|
|
+ std::string cmd("curl -H \"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36\" -LH \"Accept: text/bibliography; style=bibtex\" http://dx.doi.org/");
|
|
|
+ cmd.append(url_encode(doi));
|
|
|
+ cmd.append(" 2> /dev/null");
|
|
|
+ bibtex=exec(cmd.c_str());
|
|
|
+
|
|
|
+ for(long i=2;i<bibtex.size();i++){
|
|
|
+ if(bibtex[i-2]==-30 && bibtex[i-1]==-128 && bibtex[i]==-103){
|
|
|
+ bibtex[i-2]=32;
|
|
|
+ bibtex[i-1]=32;
|
|
|
+ bibtex[i-0]='\'';
|
|
|
+ }
|
|
|
+ if(bibtex[i-2]==-30 && bibtex[i-1]==-128 && bibtex[i]==-109){
|
|
|
+ bibtex[i-2]=32;
|
|
|
+ bibtex[i-1]=45;
|
|
|
+ bibtex[i-0]=32;
|
|
|
+ }
|
|
|
+ if(bibtex[i-2]=='}' && bibtex[i-1]==',' && bibtex[i]==' '){
|
|
|
+ bibtex[i]=10;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //bibtex=" @article{Rahimian_2015, title={Boundary integral method for the flow of vesicles with viscosity contrast in three dimensions}, volume={298}, ISSN={0021-9991}, url={http://dx.doi.org/10.1016/J.JCP.2015.06.017}, DOI={10.1016/j.jcp.2015.06.017}, journal={Journal of Computational Physics}, publisher={Elsevier BV}, author={Rahimian, Abtin and Veerapaneni, Shravan K. and Zorin, Denis and Biros, George}, year={2015}, month={Oct}, pages={766@~S786}}";
|
|
|
+ mvprintw(3*MYFIELD_COUNT+0, 0, "BIBTEX :"); wclrtoeol(win);
|
|
|
+ mvprintw(3*MYFIELD_COUNT+1, 0, bibtex.c_str()); wclrtoeol(win);
|
|
|
+ }
|
|
|
+
|
|
|
+ ch=getch();
|
|
|
+ if(ch==10){ // Open file
|
|
|
+ std::string cmd("open.sh ");
|
|
|
+ cmd.append(db[curr_field*MYFIELD_BLOCKSIZE+MYFIELD_PATH]);
|
|
|
+ exec(cmd.c_str());
|
|
|
+ }
|
|
|
+ clear();
|
|
|
+}
|
|
|
+
|
|
|
+void searchpaper(WINDOW* win, const std::vector<std::string>& db_){
|
|
|
+ std::vector<std::string> db;
|
|
|
+ std::string srch_str;
|
|
|
+
|
|
|
+ int ch='a';
|
|
|
+ int max_x,max_y;
|
|
|
+ getmaxyx(win, max_y, max_x);
|
|
|
+ clear();
|
|
|
+
|
|
|
+ MYFIELD_IDX display_fields[]={MYFIELD_TITLE,
|
|
|
+ MYFIELD_YEAR,
|
|
|
+ MYFIELD_AUTHORS,
|
|
|
+ MYFIELD_JOURNAL};
|
|
|
+
|
|
|
+ double field_wid[MYFIELD_COUNT];
|
|
|
+ for(long i=0;i<MYFIELD_COUNT;i++) field_wid[i]=0;
|
|
|
+ field_wid[MYFIELD_TITLE ]=0.50;
|
|
|
+ field_wid[MYFIELD_YEAR ]=0.05;
|
|
|
+ field_wid[MYFIELD_AUTHORS]=0.30;
|
|
|
+ field_wid[MYFIELD_JOURNAL]=0.15;
|
|
|
+
|
|
|
+ double field_dsp[MYFIELD_COUNT]; field_dsp[0]=0;
|
|
|
+ for(size_t i=1;i<MYFIELD_COUNT;i++) field_dsp[i]=field_dsp[i-1]+field_wid[i-1];
|
|
|
+
|
|
|
+ int curr_field=0;
|
|
|
+ int start_row=0;
|
|
|
+ bool update_db=true;
|
|
|
+ while(1) {
|
|
|
+ if(update_db){
|
|
|
+ db.clear();
|
|
|
+ for(size_t i=0;i<db_.size()/MYFIELD_BLOCKSIZE;i++){
|
|
|
+ bool found=(srch_str.size()==0);
|
|
|
+ for(long field=0; field<MYFIELD_COUNT; field++){
|
|
|
+ std::string str=db_[i*MYFIELD_BLOCKSIZE+field];
|
|
|
+ for(long j=0;j<str.size();j++) str[j]=std::toupper(str[j]);
|
|
|
+ if(found) break;
|
|
|
+ else found=(str.find(srch_str) != std::string::npos);
|
|
|
+ }
|
|
|
+ if(found) for(size_t field=0;field<MYFIELD_BLOCKSIZE;field++){
|
|
|
+ db.push_back(db_[i*MYFIELD_BLOCKSIZE+field]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ curr_field=0;
|
|
|
+ start_row=0;
|
|
|
+ update_db=false;
|
|
|
+ clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ // Draw
|
|
|
+ mvprintw(1, 0, "SEARCH PAPER"); wclrtoeol(win);
|
|
|
+ size_t rows=db.size()/MYFIELD_BLOCKSIZE;
|
|
|
+ if(curr_field-start_row>=max_y-3) start_row++;
|
|
|
+ if(curr_field<start_row) start_row--;
|
|
|
+ for(size_t i=start_row;i<start_row+max_y-3;i++) {
|
|
|
+ if(i>=rows) break;
|
|
|
+ if(i==curr_field) attron(A_BOLD);// | A_UNDERLINE);//COLOR_PAIR(1));
|
|
|
+ for(const MYFIELD_IDX& field : display_fields){
|
|
|
+ std::ostringstream fmt;
|
|
|
+ fmt<<"| %0."<<(int)(field_wid[field]*max_x-3)<<"s";
|
|
|
+
|
|
|
+ int offset=(int)(field_dsp[field]*max_x);
|
|
|
+ mvprintw(3+i-start_row, offset, fmt.str().c_str(), db[i*MYFIELD_BLOCKSIZE+field].c_str() );
|
|
|
+ wclrtoeol(win);
|
|
|
+ }
|
|
|
+ if(i==curr_field) attroff(A_BOLD);// | A_UNDERLINE);//COLOR_PAIR(1));
|
|
|
+ }
|
|
|
+ mvprintw(2, 0, "%s", srch_str.c_str()); wclrtoeol(win);
|
|
|
+ move(2, srch_str.size());
|
|
|
+
|
|
|
+ ch=getch();
|
|
|
+ if(ch==10 || ch==261){ // Enter
|
|
|
+ if(db.size()>0) displaypaper(win, db, curr_field);
|
|
|
+ }else if(ch==27){ // Esc
|
|
|
+ break;
|
|
|
+ }else if(ch==259){ // Up
|
|
|
+ if(curr_field>0) curr_field--;
|
|
|
+ }else if(ch==258){ // Down
|
|
|
+ if(curr_field+1<rows) curr_field++;
|
|
|
+ }else if(ch==263 || ch==330 || ch==127){ // Delete
|
|
|
+ if(srch_str.size()){
|
|
|
+ delch();
|
|
|
+ srch_str.erase(srch_str.end()-1);
|
|
|
+ update_db=true;
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ addch(ch);
|
|
|
+ char cstr[10];
|
|
|
+ sprintf(cstr,"%c",ch);
|
|
|
+ srch_str.append(cstr);
|
|
|
+ std::transform(srch_str.begin(), srch_str.end(), srch_str.begin(), ::toupper);
|
|
|
+ update_db=true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ clear();
|
|
|
+}
|
|
|
+
|
|
|
+int main(){
|
|
|
+ auto addpaper_new = [](WINDOW* win, std::string* data) {
|
|
|
+ int max_x,max_y;
|
|
|
+ getmaxyx(win, max_y, max_x);
|
|
|
+ clear();
|
|
|
+
|
|
|
+ auto select_paper = [&](std::string fname) {
|
|
|
+ clear();
|
|
|
+ mvprintw(1, 0, "SEARCHING FOR PAPERS ... "); wclrtoeol(win);
|
|
|
+ exec(std::string("open.sh " + fname).c_str());
|
|
|
+ refresh();
|
|
|
+
|
|
|
+ auto extract_search_fields = [](std::vector<std::string>& vec, std::string title, std::string field, long crop0, long crop1) {
|
|
|
+ std::string cmd("curl -H \"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36\" -Ls \"http://search.crossref.org/dois?q=\"" + url_encode(title) + "\"");
|
|
|
+ std::string lst = exec((cmd + "&rows=9&sort=score\" 2> /dev/null | grep \"\\\"" + field + "\\\":\" | rev | cut -c " + std::to_string(2 + crop1) + "- | rev | cut -c " + std::to_string(field.size() + 10 + crop0) + "- ").c_str());
|
|
|
+ auto tokenize = [](std::vector<std::string>& vec, std::string lst) {
|
|
|
+ for (long i = 0; i < lst.size(); i++) {
|
|
|
+ if (vec.size() == 0) vec.push_back("");
|
|
|
+ if ((lst[i] == 13 || lst[i] == 10) && vec.back().size() != 0) {
|
|
|
+ vec.push_back("");
|
|
|
+ } else {
|
|
|
+ vec.back().append(std::string() + lst[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (vec.size() && vec.back().size() == 0) vec.resize(vec.size()-1);
|
|
|
+ };
|
|
|
+ tokenize(vec, lst);
|
|
|
+ };
|
|
|
+ auto get_bib_fields = [](std::string& title_, std::string& year_, std::string& author_, std::string& journal_, std::string& doi_, const std::string& doi) {
|
|
|
+ if(doi.size()){ // Display bibtex
|
|
|
+ std::string bibtex;
|
|
|
+ bibtex=exec(std::string("curl -LH \"Accept: text/bibliography; style=bibtex\" http://dx.doi.org/" + url_encode(doi) + " 2> /dev/null").c_str());
|
|
|
+
|
|
|
+ auto extract_field = [](std::string str, std::string field) {
|
|
|
+ long i;
|
|
|
+ std::string out;
|
|
|
+ for (i = 0; i < str.size() - field.size() - 2; i++) {
|
|
|
+ bool matched = true;
|
|
|
+ for (long j = 0; j < field.size(); j++) {
|
|
|
+ if (str[i+j] != field[j]) {
|
|
|
+ matched = false;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (matched && str[i+field.size()+0] == '=' && str[i+field.size()+1] == '{') {
|
|
|
+ i += field.size() + 2;
|
|
|
+ for (; i < str.size(); i++) {
|
|
|
+ if (str[i] == '}') break;
|
|
|
+ out.push_back(str[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return out;
|
|
|
+ };
|
|
|
+ title_ = extract_field(bibtex, "title");
|
|
|
+ year_ = extract_field(bibtex, "year");
|
|
|
+ author_ = extract_field(bibtex, "author");
|
|
|
+ journal_ = extract_field(bibtex, "journal");
|
|
|
+ doi_ = extract_field(bibtex, "doi");
|
|
|
+ }
|
|
|
+ };
|
|
|
+ auto transform_authors = [](std::string in) {
|
|
|
+ auto tokenize = [](std::string in, std::string sep) {
|
|
|
+ std::vector<std::string> vec(1);
|
|
|
+ for (long i = 0; i < in.size(); i++) {
|
|
|
+ bool new_token = true;
|
|
|
+ for (long j = 0; j < sep.size(); j++) {
|
|
|
+ if (in[i + j] != sep[j]) {
|
|
|
+ new_token = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!new_token) {
|
|
|
+ vec.back().push_back(in[i]);
|
|
|
+ } else {
|
|
|
+ vec.push_back(std::string());
|
|
|
+ i += sep.size()-1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (vec.size() && vec.back().size() == 0) vec.resize(vec.size() - 1);
|
|
|
+ for (auto x:vec) std::cout<<x<<'\n';
|
|
|
+ return vec;
|
|
|
+ };
|
|
|
+ std::string out;
|
|
|
+ std::vector<std::string> author_vec = tokenize(in, " and ");
|
|
|
+ for (long i = 0; i < author_vec.size(); i++) {
|
|
|
+ std::vector<std::string> name_vec = tokenize(author_vec[i], ", ");
|
|
|
+ for (long j = name_vec.size()-1; j >= 0; j--) out += name_vec[j] + " ";
|
|
|
+ if (out.size()) out.resize(out.size() - 1);
|
|
|
+ out += ", ";
|
|
|
+ };
|
|
|
+ if (out.size()) out.resize(out.size() - 2);
|
|
|
+ return out;
|
|
|
+ };
|
|
|
+ std::string title = exec(("./extract_title.py " + fname).c_str());
|
|
|
+ std::vector<std::string> doi_vec, year_vec, title_vec;
|
|
|
+ extract_search_fields(doi_vec, title, "doi", 0, 1);
|
|
|
+ extract_search_fields(year_vec, title, "year", -1, -1);
|
|
|
+ extract_search_fields(title_vec, title, "title", 0, 1);
|
|
|
+ //assert(year_vec.size() == doi_vec.size());
|
|
|
+ //assert(title_vec.size() == doi_vec.size());
|
|
|
+
|
|
|
+ clear();
|
|
|
+ std::string cmd("curl -H \"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36\" -Ls \"http://search.crossref.org/dois?q=\"" + url_encode(title) + "\"" + "&rows=9&sort=score\" 2> /dev/null");
|
|
|
+ mvprintw(1, 0, "ADD PAPER"); wclrtoeol(win);
|
|
|
+ mvprintw(2, 0, "%s: %s", fname.c_str(), cmd.c_str()); wclrtoeol(win);
|
|
|
+ for (long i = 0; i < doi_vec.size(); i++) {
|
|
|
+ mvprintw(4+i, 0, "%s", (std::to_string(i+1) + ") " + year_vec[i] + " - " + title_vec[i]).c_str()); wclrtoeol(win);
|
|
|
+ }
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ int ch=getch();
|
|
|
+ long option = (long)ch - 48;
|
|
|
+ if (ch==27) {
|
|
|
+ return false;
|
|
|
+ } else if (0 < option && option <= 9) {
|
|
|
+ std::string bib_title, bib_year, bib_author, bib_journal, bib_doi;
|
|
|
+ get_bib_fields(bib_title, bib_year, bib_author, bib_journal, bib_doi, doi_vec[option-1]);
|
|
|
+
|
|
|
+ std::string field_str[MYFIELD_COUNT];
|
|
|
+ field_str[MYFIELD_TITLE ] = title_vec[option-1];
|
|
|
+ field_str[MYFIELD_YEAR ] = year_vec[option-1];
|
|
|
+ field_str[MYFIELD_AUTHORS] = transform_authors(bib_author);
|
|
|
+ field_str[MYFIELD_JOURNAL] = bib_journal;
|
|
|
+ field_str[MYFIELD_DOI ] = doi_vec[option-1];
|
|
|
+ field_str[MYFIELD_NOTES ] = "";
|
|
|
+ field_str[MYFIELD_PATH ] = fname;
|
|
|
+ for(size_t i=0;i<MYFIELD_COUNT;i++) data[i]=field_str[i];
|
|
|
+ return true;
|
|
|
+ } else if (option == 0) {
|
|
|
+ return addpaper(win, data);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ std::string srch_str;
|
|
|
+ bool update_db=true;
|
|
|
+ while(1) {
|
|
|
+ clear();
|
|
|
+ mvprintw(1, 0, "ADD PAPER"); wclrtoeol(win);
|
|
|
+ mvprintw(2, 0, "%s", srch_str.c_str()); wclrtoeol(win);
|
|
|
+ move(2, srch_str.size());
|
|
|
+ int ch=getch();
|
|
|
+ if(ch==10 || ch==261){ // Enter
|
|
|
+ auto ret = select_paper(srch_str);
|
|
|
+ clear();
|
|
|
+ if (ret) return true;
|
|
|
+ }else if(ch==27){ // Esc
|
|
|
+ break;
|
|
|
+ }else if(ch==259){ // Up
|
|
|
+ }else if(ch==258){ // Down
|
|
|
+ }else if(ch==263 || ch==330 || ch==127){ // Delete
|
|
|
+ if(srch_str.size()){
|
|
|
+ delch();
|
|
|
+ srch_str.erase(srch_str.end()-1);
|
|
|
+ update_db=true;
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ addch(ch);
|
|
|
+ char cstr[10];
|
|
|
+ sprintf(cstr,"%c",ch);
|
|
|
+ srch_str.append(cstr);
|
|
|
+ std::transform(srch_str.begin(), srch_str.end(), srch_str.begin(), ::toupper);
|
|
|
+ update_db=true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ clear();
|
|
|
+
|
|
|
+ return false;
|
|
|
+ };
|
|
|
+
|
|
|
+ initscr(); // Start curses mode
|
|
|
+ if(0) if(has_colors() == TRUE){
|
|
|
+ start_color(); // Start color
|
|
|
+ init_pair(1, COLOR_YELLOW, COLOR_BLACK);
|
|
|
+ }
|
|
|
+
|
|
|
+ WINDOW * mainwin=NULL;
|
|
|
+ int ch=0;
|
|
|
+ int max_x,max_y;
|
|
|
+ getmaxyx(mainwin, max_y, max_x);
|
|
|
+
|
|
|
+ // Initialize ncurses
|
|
|
+ if ( (mainwin = initscr()) == NULL ) {
|
|
|
+ fprintf(stderr, "Error initialising ncurses.\n");
|
|
|
+ exit(EXIT_FAILURE);
|
|
|
+ }
|
|
|
+
|
|
|
+ noecho(); // Turn off key echoing
|
|
|
+ keypad(mainwin, TRUE); // Enable the keypad for non-char keys
|
|
|
+
|
|
|
+ std::vector<std::string> db;
|
|
|
+ { // load db
|
|
|
+ std::string line;
|
|
|
+ std::ifstream dbfile("FILES/DBFILE.TXT");
|
|
|
+ if(dbfile.is_open()){
|
|
|
+ while(!dbfile.eof()){
|
|
|
+ getline(dbfile, line);
|
|
|
+ db.push_back(line);
|
|
|
+ }
|
|
|
+ dbfile.close();
|
|
|
+ }
|
|
|
+ if(db.size()) db.pop_back();
|
|
|
+ { // reverse
|
|
|
+ std::vector<std::string> db_rev=db;
|
|
|
+ for (int i = 0; i < db.size() / MYFIELD_BLOCKSIZE; i++) {
|
|
|
+ for (int j = 0; j < MYFIELD_BLOCKSIZE; j++) {
|
|
|
+ db[i * MYFIELD_BLOCKSIZE + j] = db_rev[(db.size() / MYFIELD_BLOCKSIZE - i - 1) * MYFIELD_BLOCKSIZE + j];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ while ( ch!='q' && ch!=27 ) {
|
|
|
+ refresh();
|
|
|
+ mvprintw(1, 0, "MAIN MENU"); wclrtoeol(mainwin);
|
|
|
+
|
|
|
+ ch=getch();
|
|
|
+ if(ch==27){ // Esc
|
|
|
+ mvaddstr(0, 0, "# ESC"); wclrtoeol(mainwin);
|
|
|
+ }else if(ch=='q'){ // Quit
|
|
|
+ mvaddstr(0, 0, "# Quit"); wclrtoeol(mainwin);
|
|
|
+ }else if(ch=='s'){ // Search
|
|
|
+ mvaddstr(0, 0, "# Search"); wclrtoeol(mainwin);
|
|
|
+ searchpaper(mainwin,db);
|
|
|
+ }else if(ch=='a'){ // Add
|
|
|
+ mvaddstr(0, 0, "# Add"); wclrtoeol(mainwin);
|
|
|
+ std::string data_fields[MYFIELD_COUNT];
|
|
|
+ if(addpaper_new(mainwin,data_fields)){
|
|
|
+ unsigned char md5result[MD5_DIGEST_LENGTH];
|
|
|
+ //std::string file_ext=data_fields[MYFIELD_PATH].substr(data_fields[MYFIELD_PATH].size()-4,3);
|
|
|
+ std::string file_ext=data_fields[MYFIELD_PATH].substr(data_fields[MYFIELD_PATH].find_last_of("."));
|
|
|
+ std::transform(file_ext.begin(), file_ext.end(), file_ext.begin(), ::toupper);
|
|
|
+ bool md5=!file_md5(data_fields[MYFIELD_PATH].c_str(), md5result);
|
|
|
+ if(md5){
|
|
|
+ std::string ofname;
|
|
|
+ { // get MD5 fname
|
|
|
+ ofname.append("FILES/");
|
|
|
+ for(int i=0;i<MD5_DIGEST_LENGTH;i++){
|
|
|
+ char str[10];
|
|
|
+ sprintf(str, "%02x", md5result[i]);
|
|
|
+ std::transform(str, str+2, str, ::toupper);
|
|
|
+ ofname.append(str);
|
|
|
+ }
|
|
|
+ ofname.append(file_ext);//".PDF");
|
|
|
+ }
|
|
|
+ { // move file
|
|
|
+ std::rename(data_fields[MYFIELD_PATH].c_str(), ofname.c_str());
|
|
|
+ }
|
|
|
+ for(int i=0;i<MYFIELD_COUNT-1;i++){
|
|
|
+ std::transform(data_fields[i].begin(), data_fields[i].end(), data_fields[i].begin(), ::toupper);
|
|
|
+ db.push_back(data_fields[i]);
|
|
|
+ }
|
|
|
+ db.push_back(ofname);
|
|
|
+ {
|
|
|
+ std::string s="##########################################################################################################################################################################################################";
|
|
|
+ db.push_back(s);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }else if(ch=='t'){ // Test
|
|
|
+ {
|
|
|
+ //std::string data_fields[MYFIELD_COUNT];
|
|
|
+ //for(size_t i=59;i<60;i++){
|
|
|
+ // {
|
|
|
+ // std::stringstream ss;
|
|
|
+ // ss<<i<<".pdf";
|
|
|
+ // data_fields[MYFIELD_COUNT-1]=ss.str();
|
|
|
+ // }
|
|
|
+ // unsigned char md5result[MD5_DIGEST_LENGTH];
|
|
|
+ // bool md5=!file_md5(data_fields[MYFIELD_COUNT-1].c_str(), md5result);
|
|
|
+ // if(md5){
|
|
|
+ // std::string ofname;
|
|
|
+ // { // get MD5 fname
|
|
|
+ // ofname.append("FILES/");
|
|
|
+ // for(int i=0;i<MD5_DIGEST_LENGTH;i++){
|
|
|
+ // char str[10];
|
|
|
+ // sprintf(str, "%02x", md5result[i]);
|
|
|
+ // std::transform(str, str+2, str, ::toupper);
|
|
|
+ // ofname.append(str);
|
|
|
+ // }
|
|
|
+ // ofname.append(".PDF");
|
|
|
+ // }
|
|
|
+ // { // move file
|
|
|
+ // std::rename(data_fields[MYFIELD_COUNT-1].c_str(), ofname.c_str());
|
|
|
+ // }
|
|
|
+ // for(int i=0;i<MYFIELD_COUNT-1;i++){
|
|
|
+ // std::transform(data_fields[i].begin(), data_fields[i].end(), data_fields[i].begin(), ::toupper);
|
|
|
+ // db.push_back(data_fields[i]);
|
|
|
+ // }
|
|
|
+ // db.push_back(ofname);
|
|
|
+ // {
|
|
|
+ // std::string s;
|
|
|
+ // db.push_back(s);
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ //}
|
|
|
+ }
|
|
|
+ }else if(ch=='x'){ // Export (pdf only)
|
|
|
+ {
|
|
|
+ //for(size_t i=0;i<db.size()/MYFIELD_BLOCKSIZE;i++){
|
|
|
+ // std::string ifname=db[i*MYFIELD_BLOCKSIZE+4];
|
|
|
+ // if(ifname.end()[-3]=='P' && ifname.end()[-2]=='D' && ifname.end()[-1]=='F'){
|
|
|
+ // std::string ofname="export/";
|
|
|
+ // std::string i_str=std::to_string(i);
|
|
|
+ // for(size_t j=0;j<4-i_str.size();j++) ofname.append("0");
|
|
|
+ // ofname.append(i_str);
|
|
|
+ // ofname.append("-");
|
|
|
+ // ofname.append(db[i*MYFIELD_BLOCKSIZE+0]);
|
|
|
+ // std::transform(ofname.begin(), ofname.end(), ofname.begin(), ::tolower);
|
|
|
+ // for(size_t j=0;j<ofname.size();j++) if(ofname[j]==' ') ofname[j]='-';
|
|
|
+ // ofname.append(".pdf");
|
|
|
+ // std::ifstream src(ifname.c_str());
|
|
|
+ // std::ofstream dst(ofname.c_str());
|
|
|
+ // dst<<src.rdbuf();
|
|
|
+ // }
|
|
|
+ //}
|
|
|
+ }
|
|
|
+ }else{ // Nothing
|
|
|
+ mvprintw(0, 0, "Unrecognized command: %d", ch); clrtoeol();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ { // save db
|
|
|
+ { // reverse
|
|
|
+ std::vector<std::string> db_rev=db;
|
|
|
+ for (int i = 0; i < db.size() / MYFIELD_BLOCKSIZE; i++) {
|
|
|
+ for (int j = 0; j < MYFIELD_BLOCKSIZE; j++) {
|
|
|
+ db[i * MYFIELD_BLOCKSIZE + j] = db_rev[(db.size() / MYFIELD_BLOCKSIZE - i - 1) * MYFIELD_BLOCKSIZE + j];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ std::string line;
|
|
|
+ std::ofstream dbfile("FILES/DBFILE.TXT");
|
|
|
+ if(dbfile.is_open()){
|
|
|
+ for(int i=0;i<db.size();i++) dbfile<<db[i]<<'\n';
|
|
|
+ dbfile.close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Clean up after ourselves
|
|
|
+ delwin(mainwin);
|
|
|
+ endwin();
|
|
|
+ refresh();
|
|
|
+
|
|
|
+ return EXIT_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+//mvaddstr(0, 1, "");
|
|
|
+//mvprintw(0, 1, "");
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+//#include <iostream>
|
|
|
+//#include <stdio.h>
|
|
|
+//#include <stdlib.h>
|
|
|
+//#include <curses.h>
|
|
|
+//#include <termios.h>
|
|
|
+//#include <unistd.h>
|
|
|
+//#include <termios.h>
|
|
|
+//#include <sys/ioctl.h>
|
|
|
+//#include <fcntl.h>
|
|
|
+
|
|
|
+// use system call to make terminal send all keystrokes directly to stdin
|
|
|
+//system ("/bin/stty raw");
|
|
|
+
|
|
|
+
|
|
|
+// use system call to set terminal behaviour to more normal behaviour
|
|
|
+//system ("/bin/stty cooked");
|
|
|
+
|