#include #include #include #include #include #include #include #include #define BLOG_TITLE "My blog" #define STYLESHEET_REL_PATH "blogstyle.css" //when compiling/linking, mention the object file: use "g++ blog.cpp markdown.o" extern "C" { extern char _binary_markdown_start; extern char _binary_markdown_end; } using namespace std; enum Option { NEW, EDIT, DELETE, HELP, UNKNOWN_ARG }; int newPost(); int editPost(); int deletePost(); int help(); /* GLOBAL VARIABLES - START */ char g_path[80] = "\0"; //'g' for global variable //Initialisation of all global variables void initialiseGlobalVars() { for(int i=0; i < 80; i++) g_path[i] = '\0'; } /* GLOBAL VARIABLES - END */ void bugFound() { cout << "\n\nYou have found a bug.\n" << "Please contact login@tilde.town with details on how to reproduce this bug.\n" << "Program will quit now.\n\n"; exit(2); } int checkEDITORvar() { char* editor_var = getenv("EDITOR"); if(editor_var == NULL) { cout << "\n\nThe EDITOR environment variable is not set.\n" << "Please go to 'http://askubuntu.com/questions/432524/how-do-i-find-and-set-my-editor-environment-variable' to learn how to do so."; return 1; } return 0; } int checkBlogPath() { char* home_var = getenv("HOME"); char blog_rc_path[100] = "\0"; for(int i=0; i<100; i++) blog_rc_path[i]='\0'; strcpy(blog_rc_path, home_var); strcat(blog_rc_path, "/.blogrc"); ifstream fin(blog_rc_path); if(!fin) { cout << "\n\nNo .blogrc file found in the user directory.\n" << "You must create a .blogrc file with only the path to the blog directory. This could be the public_html folder. Please include the trailing slash."; return 1; } fin.getline(g_path, 79); //79 so that we can add a slash //fix for trailing slash (if absent) int i; for(i=0; g_path[i]!='\0'; i++); --i; if(g_path[i] != '/') { ++i; g_path[i] = '/'; ++i; g_path[i] = '\0'; } DIR* dir = opendir(g_path); if(!dir) { if(ENOENT == errno) { cout << "\n\nThe user directory mentioned in .blogrc doesn't exist.\n" << "Please create \"" << g_path << "\"."; return 1; } else { cout << "\n\nThe user directory mentioned in .blogrc could not be opened.\n" << "Please ensure you have read, write and execute permissions for \"" << g_path << "\"."; return 1; } } cout << "\n\nThe directory of the index.html file is: \n" << g_path << " ."; return 0; } void strtrim(char* str) { int size=0; for(size=0; str[size]!='\0'; size++); int left_bound=0, right_bound=0; for(left_bound=0; str[left_bound]==' '; left_bound++); for(right_bound=size-1; str[right_bound]==' '; right_bound--); int j=0; for(int i=left_bound; i <= right_bound; i++, j++) { str[j] = str[i]; } str[j] = '\0'; //clean up for(int i=j; i < size; i++) { str[i] = '\0'; } } void markdown(char* input_file, char* output_file) { { char* markdown_ptr = &_binary_markdown_start; //create the markdown bashscript file ofstream fout_markdown("./blogmarkdown"); while(markdown_ptr != &_binary_markdown_end) fout_markdown.put(*markdown_ptr++); fout_markdown.close(); } system("chmod u+rx ./blogmarkdown"); char command[100] = "\0"; for(int i=0; i<100; i++) command[i]='\0'; strcpy(command, "./blogmarkdown "); strcat(command, input_file); strcat(command, " -o "); strcat(command, output_file); system(command); remove("./blogmarkdown"); } Option checkPassedArg(const int, char**); /* MAIN FUNCTION */ int main(int argc, char *argv[]) { initialiseGlobalVars(); Option choice = checkPassedArg(argc, argv); if(choice == UNKNOWN_ARG || argc > 2) { cout << "\n\nUnknown, no or too many arguments passed.\n" << "For help, pass '-h' or 'help' (without quotes) as argument.\n" << "Program will quit now.\n\n"; return 1; } int return_value = 2; switch(choice) { case NEW: return_value = newPost(); break; case EDIT: return_value = editPost(); break; case DELETE: return_value = deletePost(); break; case HELP: return_value = help(); break; default: bugFound(); break; } if(return_value == 0) { cout << "\n\nProgram executed successfully.\n" << "Program will quit now.\n\n"; return 0; } else { if(return_value == 1) cout << "\n\nError"; else cout << "\n\nUnknown error"; cout << " encountered during execution of \"" << argv[1] << "\".\n" << "Please contact login@tilde.town with details to reproduce this error.\n" << "Program will quit now.\n\n"; return 2; } cout << "\n\nUnhandled error encountered.\n" << "Please contact login@tilde.town with details to reproduce this error." << "Program will quit now.\n\n"; return 2; } Option checkPassedArg(const int argc_param, char** argv_param) { char check_list[8][7] = {"new", "-n", "edit", "-e", "delete", "-d", "help", "-h"}; if(argc_param == 1) return UNKNOWN_ARG; else { for(int i=0; i < 8; i++) if(!strcasecmp(argv_param[1], check_list[i])) { return (Option)((int)(i/2)); } } return UNKNOWN_ARG; } int newPost() { if(checkEDITORvar() || checkBlogPath()) { return 1; } else { cout << "\n\nEDITOR variable and .blogrc are okay."; } //post details (post number and post date) unsigned long int post_num = 0; char post_date[30] = "\0"; for(int i=0; i<30; i++) { post_date[i] = '\0'; } //post details - end char index_html_path[120] = "\0"; for(int i=0; i < 120; i++) index_html_path[i] = '\0'; strcpy(index_html_path, g_path); strcat(index_html_path, "index.html"); char blogstyle_css_path[120] = "\0"; for(int i=0; i < 120; i++) blogstyle_css_path[i] = '\0'; strcat(blogstyle_css_path, g_path); strcat(blogstyle_css_path, STYLESHEET_REL_PATH); ifstream fin2(index_html_path); ifstream fin3(blogstyle_css_path); if(!fin3.is_open()) //blogstyle.css doesn't exist { //index.html doesn't exist, so let's create a template if(!fin2.is_open()) { ofstream fout_index_html(index_html_path); fout_index_html << "\n\n\n \n " << BLOG_TITLE << "\n \n \n\n"; fout_index_html << " \n

" << BLOG_TITLE << "

\n\n
\n
\n"; fout_index_html << " \n
\n
\n\n
This is the footer.
\n"; fout_index_html << " \n\n"; fout_index_html.close(); } ofstream fout_blogstyle_css(blogstyle_css_path); fout_blogstyle_css << "header\n{\n border-bottom: 2px solid #CCC;\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n background-color: #FFF;\n opacity: 0.9;\n}\n\n"; fout_blogstyle_css << "header h1\n{\n text-align: center;\n font-family: monospace;\n font-weight: normal;\n}\n\n"; fout_blogstyle_css << "header a\n{\n color: inherit;\n text-decoration: none;\n}\n\n"; fout_blogstyle_css << "footer\n{\n border-top: 1px solid #CCC;\n padding: 10px 0;\n text-align: center;\n}\n\n"; fout_blogstyle_css << "main\n{\n padding: 100px;\n}\n\n"; fout_blogstyle_css << "article time\n{\n font-style: italic;\n display: list-item;\n list-style: circle outside;\n}\n\n"; fout_blogstyle_css << "article\n{\n border: 8px solid #CCC;\n border-width: 8px 0;\n padding: 15px 0 21px;\n}\n\n"; fout_blogstyle_css << "article h2\n{\n list-style: disc outside;\n display: list-item;\n}\n\n"; fout_blogstyle_css << ".placeholder\n{\n display: block;\n margin-top: -100px;\n height: 100px;\n visibility: hidden;\n pointer-events: none;\n z-index: -100;\n}\n\n"; fout_blogstyle_css << "article li\n{\n margin: 1em 0;\n}\n\n"; fout_blogstyle_css << "article:first-child\n{\n border-top: 0;\n}\n\n"; fout_blogstyle_css << "article:last-child\n{\n border-bottom: 0;\n}\n\n"; fout_blogstyle_css.close(); } fin2.close(); fin3.close(); ifstream fin(index_html_path); //finding out post number { char line[100] = "\0"; for(int i=0; i<100; i++) line[i]='\0'; char line_trunc[24] = "\0"; for(int i=0; i<24; i++) line_trunc[i]='\0'; while(fin.getline(line, 100)) { int i=0; strtrim(line); for(i=0; i<23; i++) { line_trunc[i] = line[i]; } line_trunc[i] = '\0'; if(!strcmp(line_trunc, " //"|| !fin_index_html.eof()" is there so that when there are //less than 100 characters left to read, it'll go to the body of //the while loop one last time while(fin_index_html.getline(line, 100) || !fin_index_html.eof()) { char line_trimmed[100] = "\0"; for(int i=0; i<100; i++) { line_trimmed[100] = '\0'; } strcpy(line_trimmed, line); strtrim(line_trimmed); if(strstr(line_trimmed, "\n\n"; fout_index2_html << "
\n"; fout_index2_html << " \n"; fout_index2_html << "

"; { bool first_character = true; char ch = '\0'; //writing the title out while(fin_draft.get(ch)) { if(ch == '\n') { if(first_character) //i.e., Title is empty { cout << "\n\nError. The title of the post was empty. Post was not published, but is still saved as a dotfile in the blog's directory for manual recovery. If you want to get a new draft, you should delete '" << g_path << ".draft_txt" << "'."; //cleanup fin_draft.close(); fin_index_html.close(); fout_index2_html.close(); remove(index2_html_path); //I'm leaving the draft file intact so that it can be recovered (manually) bugFound(); } break; } else fout_index2_html << ch; first_character = false; } } fout_index2_html << "

\n"; fin_draft.seekg(6, ios::cur);//skips "Date: " fin_draft.getline(post_date, 30); //getting date fin_draft.seekg(1, ios::cur); //skips the extra "\n"; char datetime[11] = "\0"; for(int i=0; i<11; i++) datetime[i] = '\0'; int datepos_i=0; if(post_date[datepos_i] >= '0' && post_date[datepos_i] <= '9') datepos_i=0; else { //day of week is mentioned in date for(datepos_i=0; post_date[datepos_i]!=' '; datepos_i++); //skips "Day-Of-Week," datepos_i++; //skips the space after the comma and lands on date of month } if((post_date[datepos_i+2]=='-' && (post_date[datepos_i+5=='-'] || post_date[datepos_i+4]=='-')) || (post_date[datepos_i+1]=='-' && (post_date[datepos_i+4]=='-' || post_date[datepos_i+3]=='-'))) //date in (d)d-(m)m-yyyy or (d)d-(m)m-yy format { if(post_date[datepos_i+1]=='-') //single digit date of month { datetime[8]='0'; datetime[9]=post_date[datepos_i]; } else { datetime[8]=post_date[datepos_i]; datepos_i++; //second digit of "date of month" datetime[9]=post_date[datepos_i]; } datetime[7]='-'; datetime[4]='-'; datepos_i+=2; //skips - and lands on month of year (expressed as a number between (0)1 and 12) if(post_date[datepos_i+1]=='-') //single digit month of year { datetime[5]='0'; datetime[6]=post_date[datepos_i]; } else { datetime[5]=post_date[datepos_i]; datepos_i++; //moving to second digit of month of year datetime[6]=post_date[datepos_i]; } datepos_i+=2; //skips - and lands on year (either expressed as a two-digit or four-digit number) if(post_date[datepos_i+2]>='0' && post_date[datepos_i+2]<='9') //year is four digits { datetime[0] = post_date[datepos_i++]; datetime[1] = post_date[datepos_i++]; datetime[2] = post_date[datepos_i++]; datetime[3] = post_date[datepos_i++]; } else { datetime[0] = '2'; //good for approximately 1000 years datetime[1] = '0'; //good for approximately 100 years datetime[2] = post_date[datepos_i++]; datetime[3] = post_date[datepos_i++]; } datetime[10] = '\0'; } else { if(post_date[datepos_i+1]==' ') //i.e., "date of month" is a single digit { datetime[8]='0'; datetime[9]=post_date[datepos_i]; } else { datetime[8]=post_date[datepos_i]; datepos_i++; //second digit of "date of month" datetime[9]=post_date[datepos_i]; } datetime[7]='-'; datetime[4]='-'; datepos_i+=2; //skips space after first/second digit and lands on "Month of Year" //for the month { char months_of_year[13][10] = { "\0", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; char month[10] = "\0"; for(int i1=0; i1<10; i1++) month[i1] = '\0'; { int j=0; for(j=0; post_date[datepos_i]!=' '; datepos_i++, j++) { month[j] = post_date[datepos_i]; } month[j]='\0'; } datepos_i++; //skip space and land on year for(int j=1; j<=12; j++) { if(!strncasecmp(months_of_year[j], month, 3)) { if(j<10) { datetime[5]='0'; datetime[6]=(char)(48+j); //48 is ascii for '0' } else { datetime[5]=(char)( 48 + (j - j%10)/10 );//tens place datetime[6]=(char)( 48 + j%10 );//ones place } break; } } //for the year datetime[0] = post_date[datepos_i++]; datetime[1] = post_date[datepos_i++]; datetime[2] = post_date[datepos_i++]; datetime[3] = post_date[datepos_i++]; datetime[10] = '\0'; } } if(post_date[datepos_i] != '\0') { cout << "\n\nError. Converting post date to datetime format failed. post_date was '" << post_date << "', datetime was '" << datetime << "' and datepos_i was '" << datepos_i << "'"; //cleanup fin_draft.close(); fin_index_html.close(); fout_index2_html.close(); remove(index2_html_path); //I'm leaving the draft file intact so that it can be recovered (manually) bugFound(); } //no 'else' because bugFound() exits the program fout_index2_html << " \n\n"; //converting the rest of the draft file to a draft2 file (which has only html post content) //then, I will convert it to html using the markdown(, ) function //then, I will copy the draft2_html file as it is to index2_html { char draft2_full_path[120] = "\0"; for(int i=0; i < 120; i++) draft2_full_path[i] = '\0'; strcpy(draft2_full_path, g_path); strcat(draft2_full_path, ".draft2_txt"); ofstream fout_draft2(draft2_full_path); char ch = '\0'; //copies the rest of the draft file to the draft2 file. while(fin_draft.get(ch)) { fout_draft2.put(ch); } //close draft file and delete draft file fin_draft.close(); remove(draft_full_path); //close draft2 file fout_draft2.close(); //converts draft2 file into draft2_html file char draft2_html_path[120] = "\0"; for(int i=0; i < 120; i++) draft2_html_path[i] = '\0'; strcpy(draft2_html_path, g_path); strcat(draft2_html_path, ".draft2_html"); markdown(draft2_full_path, draft2_html_path); ifstream fin_draft2_html(draft2_html_path); ch = '\0'; //copies the draft2_html file to index2_html while(fin_draft2_html.get(ch)) { fout_index2_html.put(ch); } //close draft2_html and draft2 files fin_draft2_html.close(); //delete draft2_html and draft2 files remove(draft2_html_path); remove(draft2_full_path); } fout_index2_html << "\n
\n"; flag_for_first_instance_of_new_post_here = 1; continue; } else { fout_index2_html << line; //copy rest of index.html to index2.html int i=0; for(i=0; line[i]!='\0'; i++); //if failbit is set, then no '\n' is required if(i<99 && !fin_index_html.fail()) { fout_index2_html << '\n'; } //this clears the failbit every time, so that istream::getline() //can continue to read in chunks of 100 chars even if the //delimiting character is not found just after those 100 chars fin_index_html.clear(fin_index_html.rdstate() & ~ios::failbit); }//end of 'else' statement }//end of while loop //remove original index file and replace it with index2_html remove(index_html_path); rename(index2_html_path, index_html_path); }//end of block /* //use xmllint to reformat the index.html file { char command[100] = "\0"; for(int i=0; i < 100; i++) command[i] = '\0'; strcat(command, "xmllint --format "); strcat(command, index_html_path); strcat(command, " --output "); strcat(command, index_html_path); system(command); } */ cout << "\n\nNew post successfully added to index.html file."; return 0; } int editPost() { if(checkEDITORvar() || checkBlogPath()) { return 1; } else { cout << "\n\nEDITOR variable and .blogrc are okay."; } cout << "\n\nSorry. Editing posts is not implemented yet. You'll have to do it manually."; return 0; } int deletePost() { cout << "\n\nSorry. Deleting is not implemented yet. You'll have to do it manually."; return 0; } int help() { cout << "\n\nSorry. Help information not written yet.\n" << "You'll have you see the source code at http://tilde.town/~login/blog.cpp.txt ."; return 0; }