/* $Id$ */ /** * edit.c, ¥Î¨Ó´£¨Ñ bbs¤Wªº¤å¦r½s¿è¾¹, §Y ve. * ²{¦b³o¤@­Ó¬O´c·d¹Lªºª©¥», ¤ñ¸û¤£Ã­©w, ¥Î¤ñ¸û¦hªº cpu, ¦ý¬O¥i¥H¬Ù¤U³\¦h * ªº°O¾ÐÅé (¥H Ptt¬°¨Ò, ¦b¤E¤d¤H¤W¯¸ªº®É­Ô, ¬ù¥i¬Ù¤U 50MB ªº°O¾ÐÅé) * ¦pªG±z»{¬°¡u®³ cpu´«°O¾ÐÅé¡v¨Ã¤£¦X¥G±zªº¶·¨D, ±z¥i¥H¦Ò¼{§ï¨Ï¥Î­×¥¿«eªº * ª©¥» (Revision 782) * * ­ì¥» ve ªº°µªk¬O, ¦]¬°¨C¤@¦æ³Ì¤j¥i¥H¿é¤J WRAPMARGIN ­Ó¦r, ©ó¬O´N´À¨C¤@ * ¦æ«O¯d¤F WRAPMARGIN ³o»ò¤jªºªÅ¶¡ (¬ù 512 bytes) . ¦ý¬O¹ê»Ú¤W, ¯¸¦b­×¥¿ * ¦¨¥»³Ì¤pªº¦Ò¶q¤W, §Ú­Ì¥u¶·­n¨Ï±o´å¼Ð©Ò¦b³o¤@¦æºû«ù WRAPMARGIN ³o»ò¤j, * ¨ä¥L¨C¤@¦æ¨ä¹ê¤£¶·­n³o»ò¦hªºªÅ¶¡. ©ó¬O³o­Ó patch´N¦b¨C¦¸´å¼Ð¦b¦æ¶¡²¾°Ê * ªº®É­Ô, ±N­ì¥»ªº¨º¦æ°O¾ÐÅéÁY¤p, ¦A±N·s²¾¨ìªº¨º¦æ­«·s¥[¤j, ¥H¹F¦¨³Ì¤pªº * °O¾ÐÅé¥Î¶q. * ¥H¤W»¡ªº³o­Ó°Ê§@¦b adjustline() ¤¤§¹¦¨, adjustline()¥t¥~¥]¬A­×¥¿¼Æ­Ó * global pointer, ¥HÁקK dangling pointer . * ¥t¥~­Y©w¸q DEBUG, ¦b textline_t µ²ºc¤¤±N¥[¤J mlength, ªí¥Ü¸Ó¦æ¹ê»Ú¦ûªº * °O¾ÐÅé¤j¤p. ¥H¤è«K´ú¸Õµ²ªG. * * XXX ¥Ñ©ó¦UºØ¾Þ§@³£¤£·| maintain °Ï¶ô¼Ð°O¼Ò¦¡ªº pointer/state, * ¦]¦¹¹ï©ó·|³y¦¨¼W§R line ªº¥\¯à, ³£±o¦Û°Ê¨ú®ø¼Ð°O¼Ò¦¡(call block_cancel()). * * 20071230 piaip * BBSmovie ¦³¤H§@¥X¤F 1.9G ªºÀÉ®×, ¬Ý¨Ó­n¤À hard limit ¸ò soft limit * [²Ä 7426572/7426572 ­¶ (100%) ¥Ø«eÅã¥Ü: ²Ä 163384551~163384573 ¦æ] * ·í¤é½Õ¬d BBSmovie ¬ÝªO»PºëµØ°Ï¡A¥­§¡Àɮ׬Ҧb 5M ¥H¤U * ³Ì¤jªº¬° 16M ªº Haruhi OP (avi ÂàÀÉ with massive ANSI) * [²Ä 2953/2953 ­¶ (100%) ¥Ø«eÅã¥Ü: ²Ä 64942~64964 ¦æ] * ¥t¥~¤¬°Ê°g®cªº¤j¤p¬° * [²Ä 1408/1408 ­¶ (100%) ¥Ø«eÅã¥Ü: ²Ä 30940~30962 ¦æ] * ¬O¥H©w¸q: * 32M ¬° size limit * 1M ¬° line limit * ¤S¡A©¿µMµo²{¤§«e totaln ¤§Ãþ³£¬O short... ©Ò¥H 65536 ´N°÷¤F? * «áµù: ¦ü¥G¬O¥Î announce ªº append §@¥X¨Óªº¡A¦³¬Ý¨ì > --- <- mark¡C * * FIXME screen resize ³y¦¨ b_lines §ïÅÜ, ¥i¯à·|³y¦¨ state ¿ù¶Ã. ¤×¨ä¬O tty * mode ÀH®É³£¥i¯à resize. TODO editor_internal_t ¦h­ÓÄæ¦ì°O screen size, * ·í¾Þ§@¨ì¤@¬q¸¨¤§«á¤~ check b_lines ¬O§_¦³§ïÅÜ. */ #include "bbs.h" #define EDIT_SIZE_LIMIT (32768*1024) #define EDIT_LINE_LIMIT (65530) // (1048576) #ifndef POST_MONEY_RATIO #define POST_MONEY_RATIO (0.5f) #endif #define ENTROPY_RATIO (0.25f) #define ENTROPY_MAX (MAX_POST_MONEY/ENTROPY_RATIO) #if 0 #define register #define DEBUG #define inline #endif /** * data Äæ¦ìªº¥Îªk: * ¨C¦¸ allocate ¤@­Ó textline_t ®É¡A·|°tµ¹¥L (sizeof(textline_t) + string * length - 1) ªº¤j¤p¡C¦p¦¹¥iª½±µ¦s¨ú data ¦Ó¤£»ÝÃB¥~ªº malloc¡C */ typedef struct textline_t { struct textline_t *prev; struct textline_t *next; short len; #ifdef DEBUG short mlength; #endif char data[1]; } textline_t; #define KEEP_EDITING -2 enum { NOBODY, MANAGER, SYSOP }; /** * ³o­Ó»¡©ú·|±N¾ã­Ó edit.c ¹B§@ªº·§©À±a¹L¡A¥D­n·|±q editor_internal_t ªº * data structure ½Í°_¡C¹ï©ó¨C¤@­Ó data member ªº¸Ô²Ó¥\¯à¡A½Ð¨£ sturcture * ¤¤ªºµù¸Ñ¡C * * ¤å³¹ªº¤º®e (¥H¤UºÙ content) ¥H¡u¦æ¡v¬°³æ¦ì¡A¥D­n¥H firstline, lastline, * totaln ¨Ó°O¿ý¡C * * User ¦bµe­±¤¤¬Ý¨ìªºµe­±¡A¸m©ó¤@­Ó¡u window ¡v¤¤¡A³o­Ó window ·|¦b * content ¤W²¾°Ê¡Awindow ¸Ì­±ªº½d³ò§Y¬°­n show ¥X¨Óªº½d³ò¡C¥¦¥Î¤F¤£¤Ö * Äæ¦ì¨Ó°O¿ý¡A¥]¬A currline, top_of_win, currln, currpnt, curr_window_line, * edit_margin¡CÅã¥Ü¥X¨Óªº®ÄªG·íµM¤£¥u¬O¾a³o´X­Ó¸ê®Æ¡AÁÙ·|¸ò¨ä¥LÄæ¦ì¦³¥æ¤¬ * §@¥Î¡A¨Ò¦p ±m¦â½s¿è¼Ò¦¡¡B¯S®í²Å¸¹½s¿è µ¥µ¥¡C¨ä¤¤³Ì½ÆÂøªº³¡¤À¬O¦b¿ï¨ú block * ¡]¨£«á¡^ªº®É­Ô¡C * * editor ªº¨Ï¥Î¤W¥Ø«e¦³¤­ºØ inclusive ªº mode¡G * insert mode: * ´¡¤J/¨ú¥N * ansi mode: * ±m¦â½s¿è * indent mode: * ¦Û°ÊÁY±Æ * phone mode: * ¯S®í²Å¸¹½s¿è * raw mode: * ignore Ctrl('S'), Ctrl('Q'), Ctrl('T') * ÃÙ¤ê: ³o¦³¤°»ò¥Î? ¬Ý°_¨Ó¬O modem ¤W¶Ç¥Î (¨S¤H¦b¥Î³o­Ó¤F§a) * ®³¨Ó·í dbcs option §a * * editor ¤ä´©¤F°Ï¶ô¿ï¾Üªº¥\¯à¡]¦h¦æ¿ï¨ú ©Î ³æ¦æ¤¤ªº¤ù¬q¡^¡A¹ï©ó¤@­Ó selected * block¡A¥i¥H cut, copy, cancel, ©ÎªÌ¦s¨ì¼È¨úÀÉ¡A¬Æ¦Ü¬O©¹¥ª/¥k shift¡C¸Ô¨£ * block_XXX¡C * * ¥Î Ctrl('Y') §R°£ªº¨º¤@¦æ·|³Q°O¨ì deleted_line ³o­ÓÄæ¦ì¤¤¡Cundelete_line() * ¥i¥H°µ undelete ªº°Ê§@¡C deleted_line ªì©l­È¬° NULL¡A¨C¦¸¥u¤¹³\¦s¤@¦æ¡A©Ò¥H * ¦b¦s¤U¨Ó®É¡Aµo²{­È¤£¬° NULL ·|¥ý°µ free ªº°Ê§@¡Ceditor µ²§ô®É¡A¦b * edit_buffer_destructor() ¤¤¦pªGµo²{¦³ deleted_line¡A·|¦b³oÃäÄÀ©ñ±¼¡C¨ä¥L¦a * ¤è¤]¦³¬Û¦Pªº§@ªk¡A¨Ò¦p searched_string¡Csearched_string ¬O¦b·j´M¤å³¹¤º®e * ®É¡A­n´M§äªº key word¡C * * ÁÙ¦³¤@­Ó¦³½ìªº¯SÂI¡A¡u¬A¸¹¤Ç°t¡v¡I¦æ¬°´N¦p¦P¦b vim ¸Ì­±¤@¼Ë¡C§c¡A·íµM¨S¨º * »ò±j°Õ¡A¦ý¦Ü¤Ö¦b§t¦³ c-style comment ¸ò c-style string ®É¬O¹ïªº³á¡C³o­Ó°Ê * §@©w¸q©ó match_paren() ¤¤¡C * * ¥t¥~¡A¦pªG¦³»Ý­n·s¼W·sªºÄæ¦ì¡A½Ð±Nªì©l¤Æ¡]¦³»Ý­nªº¸Ü¡^ªº°Ê§@¼g¦b * edit_buffer_constructor ¤¤¡C·íµM¤]¦³­Ó edit_buffer_destructor ¥i¥H¨Ï¥Î¡C * * ¦¹¥~¡A¬°¤F´£¨Ñ¤@­Ó reentrant ªº editor¡Aprev «ü¦V«e¤@­Ó editor ªº * editor_internal_t¡Center_edit_buffer ¸ò exit_edit_buffer ´£¨Ñ¶i¥Xªº¤¶­±¡A * ¸Ì­±¤À§O·|©I¥s constructor ¸ò destructor¡C * * TODO * vedit ¸Ì­±¦³­Ó curr_buf->oldcurrline¡A¥Î¨Ó°O¤W¤@¦¸ªº currline¡C¥Ñ©ó¥u¦³ currline ¾Ö * ¦³ WRAPMARGIN ªºªÅ¶¡¡A©Ò¥H¥Ø«eªº§@ªk¬O·í curr_buf->oldcurrline != currline ®É¡A´N * resize curr_buf->oldcurrline ¸ò currline¡C¦ý¬OÁV¿|ªº¬O¥Ø«e¥²¶·¤H¤u°lÂÜ currline ªº¦æ * ¬°¡A¦Ó¥B­Y¤£©¯¹J¨ì curr_buf->oldcurrline «ü¨ìªº¨º¤@¦æ¤w¸g³Q free ±¼¡A´N§¹¤F¡C³Ì¦n¬O * §â³o¨ÇªF¦è¥]°_¨Ó¡C¤£¹L§Ú¨SªÅ°µ¤F¡Apatch is welcome :P * * Victor Hsieh * Thu, 03 Feb 2005 15:18:00 +0800 */ typedef struct editor_internal_t { textline_t *firstline; /* first line of the article. */ textline_t *lastline; /* last line of the article. */ textline_t *currline; /* current line of the article(window). */ textline_t *blockline; /* the first selected line of the block. */ textline_t *top_of_win; /* top line of the article in the window. */ textline_t *deleted_line; /* deleted line. Just keep one deleted line. */ textline_t *oldcurrline; int currln; /* current line of the article. */ short currpnt; /* current column of the article. */ int totaln; /* last line of the article. */ int curr_window_line; /* current line to the window. */ short last_margin; short edit_margin; /* when the cursor moves out of range (say, t_columns), shift this length of the string so you won't see the first edit_margin-th character. */ short lastindent; int blockln; /* the row you started to select block. */ char last_phone_mode; char ifuseanony :1; char redraw_everything :1; char insert_mode :1; char ansimode :1; char indent_mode :1; char phone_mode :1; char raw_mode :1; char *searched_string; char *sitesig_string; char *(*substr_fp) (); char synparser; // syntax parser struct editor_internal_t *prev; } editor_internal_t; // } __attribute__ ((packed)) static editor_internal_t *curr_buf = NULL; static const char * const fp_bak = "bak"; // forward declare static inline int has_block_selection(void); static textline_t * alloc_line(short length); static void block_cancel(void); static const char * const BIG5[13] = { "¡A¡F¡G¡B¡N¡C¡H¡I¡E¡T¡]¡^¡©¡ª¡«¡¬", "¢b¢c¢d¢e¢f¢g¢h¢i¢j¢k¢l¢m¢n¢o¢pùþ ", "¡³¡ó¡·¡´¡¸¡¹¡¼¡½¡¿¡¶¡¾¡µ¡º¡»¡ð¡ñ", "¡Ë¡\\¡[¡Â¡Ä¡X¡ü¡ý¢y¡þ¢@¢®¢¬¢­¢A¢B", "¡Ï¡Ð¡Ñ¡Ò¡Ô¡Ó¡×¡Ý¡Ú¡Ü¡Ø¡Ù¡Õ¡Ö¡î¡ï", "¡Û¡ã¡ä¡å¡ì¡í¡®¡æ¡ç¡è¡é¡Þ¡ß¡à¡á¡â", "¡ô¡õ¡ö¡÷¡ø¡ù¡ú¡û", "¡i¡j¡u¡v¡y¡z¡q¡r¡m¡n¡e¡f¡a¡b¡_¡`", "¡g¡h¡c¡d¡k¡l¡s¡t¡o¡p¡w¡x¡{¡|", "¢¨¢©¢ª¢«¡Î¡¯¡°¡±¢I¡ò¡À¡K¡L¡Æ¡È", "£\\£]£^£_£`£a£b£c£d£e£f£g£h£i£j£k", "£l£m£n£o£p£q£r£s£G£K£N£S£U£X£Z£[", "¢¹¢º¢»¢¼¢½¢¾¢¿¢À¢Á¢Â" }; static const char * const BIG_mode[13] = { "¼ÐÂI", "¹Ï¶ô", "¼Ð°O", "¼Ð½u", "¼Æ¤@", "¼Æ¤G", "½bÀY", "¬A¤@", "¬A¤G", "¨ä¥L", "§Æ¤@", "§Æ¤G", "¼Æ¦r" }; static const char *table[8] = { "¢x¢w¢|¢r¢}¢u¢q¢t¢z¢s¢{", "ùø¢¤ùãùäùåùàùáùâùÝùÞùß", "ùø¢wùõùöù÷ùòùóùôùïùðùñ", "¢x¢¤ùìùíù¢¦¢§ùæùçùè", "¢x¢w¢¢¢r¢£¢u¢q¢t¢~¢s¢¡", "ùø¢¤¢¢ù䢣ùàùáùâ¢~ùÞ¢¡", "ùø¢wùõùöù÷ùòùóùôùïùðùñ", "¢x¢¤ùìùíù¢¦¢§ùæùçùè" }; static const char *table_mode[6] = { "ª½¨¤", "Ås©·", "¢q", "ùá", "ùó", "¢¦" }; #ifdef DBCSAWARE static char mbcs_mode =1; #define IS_BIG5_HI(x) (0x81 <= (x) && (x) <= 0xfe) #define IS_BIG5_LOS(x) (0x40 <= (x) && (x) <= 0x7e) #define IS_BIG5_LOE(x) (0x80 <= (x) && (x) <= 0xfe) #define IS_BIG5_LO(x) (IS_BIG5_LOS(x) || IS_BIG5_LOE(x)) #define IS_BIG5(hi,lo) (IS_BIG5_HI(hi) && IS_BIG5_LO(lo)) int mchar_len(unsigned char *str) { return ((str[0] != '\0' && str[1] != '\0' && IS_BIG5(str[0], str[1])) ? 2 : 1); } #define FC_RIGHT (0) #define FC_LEFT (~FC_RIGHT) int fix_cursor(char *str, int pos, unsigned int dir) { int newpos, w; for(newpos = 0; *str != '\0' && (w = mchar_len((unsigned char*)str), newpos + 1 + (dir & (w - 1))) <= pos; str += w, newpos += w) ; return newpos; } #endif /* °O¾ÐÅéºÞ²z»P½s¿è³B²z */ static inline void edit_buffer_constructor(editor_internal_t *buf) { /* all unspecified columns are 0 */ buf->blockln = -1; buf->insert_mode = 1; buf->redraw_everything = 1; buf->lastindent = -1; buf->oldcurrline = buf->currline = buf->top_of_win = buf->firstline = buf->lastline = alloc_line(WRAPMARGIN); } static inline void enter_edit_buffer(void) { editor_internal_t *p = curr_buf; curr_buf = (editor_internal_t *)malloc(sizeof(editor_internal_t)); memset(curr_buf, 0, sizeof(editor_internal_t)); curr_buf->prev = p; edit_buffer_constructor(curr_buf); } static inline void free_line(textline_t *p) { p->next = (textline_t*)0x12345678; p->prev = (textline_t*)0x87654321; p->len = -12345; free(p); } static inline void edit_buffer_destructor(void) { textline_t *p, *pnext; for (p = curr_buf->firstline; p; p = pnext) { pnext = p->next; free_line(p); } if (curr_buf->deleted_line != NULL) free_line(curr_buf->deleted_line); if (curr_buf->searched_string != NULL) free(curr_buf->searched_string); if (curr_buf->sitesig_string != NULL) free(curr_buf->sitesig_string); } static inline void exit_edit_buffer(void) { editor_internal_t *p = curr_buf; edit_buffer_destructor(); curr_buf = p->prev; free(p); } /** * transform position ansix in an ansi string of textline_t to the same * string without escape code. * @return position in the string without escape code. */ static int ansi2n(int ansix, textline_t * line) { register char *data, *tmp; register char ch; data = tmp = line->data; while (*tmp) { if (*tmp == KEY_ESC) { while ((ch = *tmp) && !isalpha((int)ch)) tmp++; if (ch) tmp++; continue; } if (ansix <= 0) break; tmp++; ansix--; } return tmp - data; } /** * opposite to ansi2n, according to given textline_t. * @return position in the string with escape code. */ static short n2ansi(short nx, textline_t * line) { register short ansix = 0; register char *tmp, *nxp; register char ch; tmp = nxp = line->data; nxp += nx; while (*tmp) { if (*tmp == KEY_ESC) { while ((ch = *tmp) && !isalpha((int)ch)) tmp++; if (ch) tmp++; continue; } if (tmp >= nxp) break; tmp++; ansix++; } return ansix; } /* ¿Ã¹õ³B²z¡G»²§U°T®§¡BÅã¥Ü½s¿è¤º®e */ static inline void show_phone_mode_panel(void) { int i; move(b_lines - 1, 0); clrtoeol(); if (curr_buf->last_phone_mode < 20) { int len; prints(ANSI_COLOR(1;46) "¡i%s¿é¤J¡j ", BIG_mode[curr_buf->last_phone_mode - 1]); len = strlen(BIG5[curr_buf->last_phone_mode - 1]) / 2; for (i = 0; i < len; i++) prints(ANSI_COLOR(37) "%c" ANSI_COLOR(34) "%2.2s", i + 'A', BIG5[curr_buf->last_phone_mode - 1] + i * 2); for (i = 0; i < 16 - len; i++) outs(" "); outs(ANSI_COLOR(37) " `1~9-=¤Á´« Zªí®æ" ANSI_RESET); } else { prints(ANSI_COLOR(1;46) "¡iªí®æø»s¡j /=%s *=%s§Î ", table_mode[(curr_buf->last_phone_mode - 20) / 4], table_mode[(curr_buf->last_phone_mode - 20) % 4 + 2]); for (i = 0;i < 11;i++) prints(ANSI_COLOR(37) "%c" ANSI_COLOR(34) "%2.2s", i ? i + '/' : '.', table[curr_buf->last_phone_mode - 20] + i * 2); outs(ANSI_COLOR(37) " Z¤º½X " ANSI_RESET); } } /** * Show the bottom status/help bar, and BIG5/table in phone_mode. */ static void edit_msg(void) { char buf[STRLEN]; int n = curr_buf->currpnt; if (curr_buf->ansimode) /* Thor: §@ ansi ½s¿è */ n = n2ansi(n, curr_buf->currline); if (curr_buf->phone_mode) show_phone_mode_panel(); snprintf(buf, sizeof(buf), " (^Z/F1)»¡©ú (^P/^G)´¡¤J²Å¸¹/¹Ï¤ù (^X/^Q)Â÷¶}\t" "ùø%s¢x%c%c%c%cùø%3d:%3d", curr_buf->insert_mode ? "´¡¤J" : "¨ú¥N", curr_buf->ansimode ? 'A' : 'a', curr_buf->indent_mode ? 'I' : 'i', curr_buf->phone_mode ? 'P' : 'p', curr_buf->raw_mode ? 'R' : 'r', curr_buf->currln + 1, n + 1); vs_footer(" ½s¿è¤å³¹ ", buf); } //#define SLOW_CHECK_DETAIL static void edit_buffer_check_healthy(textline_t *line) { assert(line); if (line->next) assert(line->next->prev == line); if (line->prev) assert(line->prev->next == line); assert(0 <= line->len); assert(line->len <= WRAPMARGIN); #ifdef DEBUG assert(line->len <= line->mlength); #endif #ifdef SLOW_CHECK_DETAIL assert(strlen(line->data) == line->len); #endif } static inline int visible_window_height(void); static void edit_check_healthy() { #ifdef SLOW_CHECK_DETAIL int i; textline_t *p; #endif edit_buffer_check_healthy(curr_buf->firstline); assert(curr_buf->firstline->prev == NULL); edit_buffer_check_healthy(curr_buf->lastline); assert(curr_buf->lastline->next == NULL); edit_buffer_check_healthy(curr_buf->oldcurrline); // currline edit_buffer_check_healthy(curr_buf->currline); assert(0 <= curr_buf->currpnt && curr_buf->currpnt <= curr_buf->currline->len); if (curr_buf->deleted_line) { edit_buffer_check_healthy(curr_buf->deleted_line); assert(curr_buf->deleted_line->next == NULL); assert(curr_buf->deleted_line->prev == NULL); assert(curr_buf->deleted_line != curr_buf->firstline); assert(curr_buf->deleted_line != curr_buf->lastline); assert(curr_buf->deleted_line != curr_buf->currline); assert(curr_buf->deleted_line != curr_buf->blockline); assert(curr_buf->deleted_line != curr_buf->top_of_win); } // lines assert(0 <= curr_buf->currln); assert(curr_buf->currln <= curr_buf->totaln); // window assert(curr_buf->curr_window_line < visible_window_height()); #ifdef SLOW_CHECK_DETAIL // firstline -> currline (0 -> currln) p = curr_buf->firstline; for (i = 0; i < curr_buf->currln; i++) { edit_buffer_check_healthy(p); p = p->next; } assert(p == curr_buf->currline); // currline -> lastline (currln -> totaln) p = curr_buf->currline; for (i = 0; i < curr_buf->totaln - curr_buf->currln; i++) { edit_buffer_check_healthy(p); p = p->next; } assert(p == curr_buf->lastline); // top_of_win -> currline (curr_window_line) p = curr_buf->top_of_win; for (i = 0; i < curr_buf->curr_window_line; i++) p = p->next; assert(p == curr_buf->currline); #endif // block assert(curr_buf->blockln < 0 || curr_buf->blockline); if (curr_buf->blockline) { edit_buffer_check_healthy(curr_buf->blockline); #ifdef SLOW_CHECK_DETAIL p = curr_buf->firstline; for (i = 0; i < curr_buf->blockln; i++) p = p->next; assert(p == curr_buf->blockline); #endif } } /** * return the middle line of the window. */ static inline int middle_line(void) { return p_lines / 2 + 1; } /** * Return the previous 'num' line. Stop at the first line if there's * not enough lines. */ static textline_t * back_line(textline_t * pos, int num, bool changeln) { while (num-- > 0) { register textline_t *item; if (pos && (item = pos->prev)) { pos = item; if (changeln) curr_buf->currln--; } else break; } return pos; } static inline int visible_window_height(void) { if (curr_buf->phone_mode) return b_lines - 1; else return b_lines; } /** * Return the next 'num' line. Stop at the last line if there's not * enough lines. */ static textline_t * forward_line(textline_t * pos, int num, bool changeln) { while (num-- > 0) { register textline_t *item; if (pos && (item = pos->next)) { pos = item; if (changeln) curr_buf->currln++; } else break; } return pos; } /** * move the cursor to the next line with ansimode fixed. */ static inline void cursor_to_next_line(void) { short pos; if (curr_buf->currline->next == NULL) return; curr_buf->currline = curr_buf->currline->next; curr_buf->curr_window_line++; curr_buf->currln++; if (curr_buf->ansimode) { pos = n2ansi(curr_buf->currpnt, curr_buf->currline->prev); curr_buf->currpnt = ansi2n(pos, curr_buf->currline); } else { curr_buf->currpnt = (curr_buf->currline->len > curr_buf->lastindent) ? curr_buf->lastindent : curr_buf->currline->len; } } /** * opposite to cursor_to_next_line. */ static inline void cursor_to_prev_line(void) { short pos; if (curr_buf->currline->prev == NULL) return; curr_buf->curr_window_line--; curr_buf->currln--; curr_buf->currline = curr_buf->currline->prev; if (curr_buf->ansimode) { pos = n2ansi(curr_buf->currpnt, curr_buf->currline->next); curr_buf->currpnt = ansi2n(pos, curr_buf->currline); } else { curr_buf->currpnt = (curr_buf->currline->len > curr_buf->lastindent) ? curr_buf->lastindent : curr_buf->currline->len; } } static inline void edit_window_adjust(void) { int offset = 0; if (curr_buf->curr_window_line < 0) { offset = curr_buf->curr_window_line; curr_buf->curr_window_line = 0; curr_buf->top_of_win = curr_buf->currline; } if (curr_buf->curr_window_line >= visible_window_height()) { offset = curr_buf->curr_window_line - visible_window_height() + 1; curr_buf->curr_window_line = visible_window_height() - 1; curr_buf->top_of_win = back_line(curr_buf->currline, visible_window_height() - 1, false); } if (offset == -1) rscroll(); else if (offset == 1) { move(visible_window_height(), 0); clrtoeol(); scroll(); } else if (offset != 0) { curr_buf->redraw_everything = YEA; } } static inline void edit_window_adjust_middle(void) { if (curr_buf->currln < middle_line()) { curr_buf->top_of_win = curr_buf->firstline; curr_buf->curr_window_line = curr_buf->currln; } else { int i; textline_t *p = curr_buf->currline; curr_buf->curr_window_line = middle_line(); for (i = curr_buf->curr_window_line; i; i--) p = p->prev; curr_buf->top_of_win = p; } curr_buf->redraw_everything = YEA; } /** * Get the current line number in the window now. */ static int get_lineno_in_window(void) { int cnt = 0; textline_t *p = curr_buf->currline; while (p && (p != curr_buf->top_of_win)) { cnt++; p = p->prev; } return cnt; } /** * shift given raw data s with length len to left by one byte. */ static void raw_shift_left(char *s, int len) { int i; for (i = 0; i < len && s[i] != 0; ++i) s[i] = s[i + 1]; } /** * shift given raw data s with length len to right by one byte. */ static void raw_shift_right(char *s, int len) { int i; for (i = len - 1; i >= 0; --i) s[i + 1] = s[i]; } /** * Return the pointer to the next non-space position. */ static char * next_non_space_char(char *s) { while (*s == ' ') s++; return s; } /** * allocate a textline_t with length length. */ static textline_t * alloc_line(short length) { textline_t *p; if ((p = (textline_t *) malloc(length + sizeof(textline_t)))) { memset(p, 0, length + sizeof(textline_t)); #ifdef DEBUG p->mlength = length; #endif return p; } assert(p); abort_bbs(0); return NULL; } /** * clone a textline_t */ static textline_t * clone_line(const textline_t *line) { textline_t *p; p = alloc_line(line->len); p->len = line->len; strlcpy(p->data, line->data, p->len + 1); return p; } /** * Insert p after line in list. Keeps up with last line */ static void insert_line(textline_t *line, textline_t *p) { textline_t *n; if ((p->next = n = line->next)) n->prev = p; else curr_buf->lastline = p; line->next = p; p->prev = line; } /** * delete_line deletes 'line' from the line list. * @param saved true if you want to keep the line in deleted_line */ static void delete_line(textline_t * line, int saved) { register textline_t *p = line->prev; register textline_t *n = line->next; if (!p && !n) { line->data[0] = line->len = 0; return; } assert(line != curr_buf->top_of_win); if (n) n->prev = p; else curr_buf->lastline = p; if (p) p->next = n; else curr_buf->firstline = n; curr_buf->totaln--; if (saved) { if (curr_buf->deleted_line != NULL) free_line(curr_buf->deleted_line); curr_buf->deleted_line = line; curr_buf->deleted_line->next = NULL; curr_buf->deleted_line->prev = NULL; } else { free_line(line); } } /** * Return the indent space number according to CURRENT line and the FORMER * line. It'll be the first line contains non-space character. * @return space number from the beginning to the first non-space character, * return 0 if non or not in indent mode. */ static int indent_space(void) { textline_t *p; int spcs; if (!curr_buf->indent_mode) return 0; for (p = curr_buf->currline; p; p = p->prev) { for (spcs = 0; p->data[spcs] == ' '; ++spcs); /* empty loop */ if (p->data[spcs]) return spcs; } return 0; } /** * adjustline(oldp, len); * ¥Î¨Ó±N oldp «ü¨ìªº¨º¤@¦æ, ­«·s­×¥¿¦¨ len³o»òªø. * * ©I¥s¤F adjustline «á°O±oÀˬd¦³°Ê¨ì currline, ¦pªG¬Oªº¸Ü oldcurrline ¤]­n°Ê * * In FreeBSD: * ¦b³oÃä¤@¦@°µ¤F¨â¦¸ªº memcpy() , ²Ä¤@¦¸±q heap «þ¨ì stack , * §â­ì¨Ó°O¾ÐÅé free() «á, ¤S­«·s¦b stack¤W malloc() ¤@¦¸, * µM«á¦A«þ¨©¦^¨Ó. * ¥D­n¬O¥Î sbrk() Æ[¹î¨ìªºµ²ªG, ³o¼Ë¤l¤~¯uªº¯àÁY´î°O¾ÐÅé¥Î¶q. * ¸Ô¨£ /usr/share/doc/papers/malloc.ascii.gz (in FreeBSD) */ static textline_t * adjustline(textline_t *oldp, short len) { // XXX write a generic version ? char tmpl[sizeof(textline_t) + WRAPMARGIN]; textline_t *newp; assert(0 <= oldp->len && oldp->len <= WRAPMARGIN); memcpy(tmpl, oldp, oldp->len + sizeof(textline_t)); free_line(oldp); newp = alloc_line(len); memcpy(newp, tmpl, len + sizeof(textline_t)); if( oldp == curr_buf->firstline ) curr_buf->firstline = newp; if( oldp == curr_buf->lastline ) curr_buf->lastline = newp; if( oldp == curr_buf->currline ) curr_buf->currline = newp; if( oldp == curr_buf->blockline ) curr_buf->blockline = newp; if( oldp == curr_buf->top_of_win) curr_buf->top_of_win= newp; if( newp->prev != NULL ) newp->prev->next = newp; if( newp->next != NULL ) newp->next->prev = newp; // vmsg("adjust %x to %x, length: %d", (int)oldp, (int)newp, len); return newp; } /** * split 'line' right before the character pos * * @return the latter line after splitting */ static textline_t * split(textline_t * line, int pos) { if (pos <= line->len) { register textline_t *p = alloc_line(WRAPMARGIN); register char *ptr; int spcs = indent_space(); curr_buf->totaln++; p->len = line->len - pos + spcs; line->len = pos; memset(p->data, ' ', spcs); p->data[spcs] = 0; ptr = line->data + pos; if (curr_buf->indent_mode) ptr = next_non_space_char(ptr); strcat(p->data + spcs, ptr); ptr[0] = '\0'; if (line == curr_buf->currline && pos <= curr_buf->currpnt) { line = adjustline(line, line->len); insert_line(line, p); // because p is allocated with fullsize, we can skip adjust. // curr_buf->oldcurrline = line; curr_buf->oldcurrline = curr_buf->currline = p; if (pos == curr_buf->currpnt) curr_buf->currpnt = spcs; else curr_buf->currpnt -= pos; curr_buf->curr_window_line++; curr_buf->currln++; /* split may cause cursor hit bottom */ edit_window_adjust(); } else { p = adjustline(p, p->len); insert_line(line, p); } curr_buf->redraw_everything = YEA; } return line; } /** * Insert a character ch to current line. * * The line will be split if the length is >= WRAPMARGIN. It'll be split * from the last space if any, or start a new line after the last character. */ static void insert_char(int ch) { register textline_t *p = curr_buf->currline; register int i = p->len; register char *s; int wordwrap = YEA; assert(curr_buf->currpnt <= i); block_cancel(); if (curr_buf->currpnt < i && !curr_buf->insert_mode) { p->data[curr_buf->currpnt++] = ch; /* Thor: ansi ½s¿è, ¥i¥Hoverwrite, ¤£»\¨ì ansi code */ if (curr_buf->ansimode) curr_buf->currpnt = ansi2n(n2ansi(curr_buf->currpnt, p), p); } else { raw_shift_right(p->data + curr_buf->currpnt, i - curr_buf->currpnt + 1); p->data[curr_buf->currpnt++] = ch; i = ++(p->len); } if (i < WRAPMARGIN) return; s = p->data + (i - 1); while (s != p->data && *s == ' ') s--; while (s != p->data && *s != ' ') s--; if (s == p->data) { wordwrap = NA; s = p->data + (i - 2); } p = split(p, (s - p->data) + 1); p = p->next; i = p->len; if (wordwrap && i >= 1) { if (p->data[i - 1] != ' ') { p->data[i] = ' '; p->data[i + 1] = '\0'; p->len++; } } } /** * insert_char twice. */ static void insert_dchar(const char *dchar) { insert_char(*dchar); insert_char(*(dchar+1)); } static void insert_tab(void) { do { insert_char(' '); } while (curr_buf->currpnt & 0x7); } /** * Insert a string. * * All printable and ESC_CHR will be directly printed out. * '\t' will be printed to align every 8 byte. * '\n' will split the line. * The other character will be ignore. */ static void insert_string(const char *str) { char ch; block_cancel(); while ((ch = *str++)) { if (isprint2(ch) || ch == ESC_CHR) insert_char(ch); else if (ch == '\t') insert_tab(); else if (ch == '\n') split(curr_buf->currline, curr_buf->currpnt); } } /** * undelete the deleted line. * * return NULL if there's no deleted_line, otherwise, return currline. */ static textline_t * undelete_line(void) { textline_t *p; if (!curr_buf->deleted_line) return NULL; block_cancel(); p = clone_line(curr_buf->deleted_line); // insert in front of currline p->prev = curr_buf->currline->prev; p->next = curr_buf->currline->next; if (curr_buf->currline->prev) curr_buf->currline->prev->next = p; curr_buf->currline->prev = p; curr_buf->totaln++; curr_buf->currline = adjustline(curr_buf->currline, curr_buf->currline->len); // maintain special line pointer if (curr_buf->top_of_win == curr_buf->currline) curr_buf->top_of_win = p; if (curr_buf->firstline == curr_buf->currline) curr_buf->firstline = p; // change currline curr_buf->currline = p; curr_buf->currline = adjustline(curr_buf->currline, WRAPMARGIN); curr_buf->currpnt = 0; curr_buf->redraw_everything = YEA; return curr_buf->currline; } /* * join $line and $line->next * * line: A1 A2 * next: B1 B2 * ....: C1 C2 * * case B=empty: * return YEA * * case A+B < WRAPMARGIN: * line: A1 A2 B1 B2 * next: C1 C2 * return YEA * NOTE It assumes $line has allocated WRAPMARGIN length of data buffer. * * case A+B1+B2 > WRAPMARGIN, A+B1next)) return YEA; if (!*next_non_space_char(n->data)) return YEA; ovfl = line->len + n->len - WRAPMARGIN; if (ovfl < 0) { strcat(line->data, n->data); line->len += n->len; delete_line(n, 0); return YEA; } else { register char *s; /* the split point */ s = n->data + n->len - ovfl - 1; while (s != n->data && *s == ' ') s--; while (s != n->data && *s != ' ') s--; if (s == n->data) return YEA; split(n, (s - n->data) + 1); assert(line->len + line->next->len < WRAPMARGIN); join(line); n = line->next; ovfl = n->len - 1; if (ovfl >= 0 && ovfl < WRAPMARGIN - 2) { s = &(n->data[ovfl]); if (*s != ' ') { strcat(s, " "); n->len++; } } line->next=adjustline(line->next, WRAPMARGIN); join(line->next); line->next=adjustline(line->next, line->next->len); return NA; } } static void delete_char(void) { register int len; if ((len = curr_buf->currline->len)) { assert(curr_buf->currpnt < len); raw_shift_left(curr_buf->currline->data + curr_buf->currpnt, curr_buf->currline->len - curr_buf->currpnt + 1); curr_buf->currline->len--; } } static void load_file(FILE * fp, off_t offSig) { char buf[WRAPMARGIN + 2]; int indent_mode0 = curr_buf->indent_mode; size_t szread = 0; assert(fp); curr_buf->indent_mode = 0; while (fgets(buf, sizeof(buf), fp)) { szread += strlen(buf); if (offSig < 0 || szread <= offSig) { insert_string(buf); } else { // this is the site sig break; } } curr_buf->indent_mode = indent_mode0; } /* ¼È¦sÀÉ */ const char * ask_tmpbuf(int y) { static char fp_buf[] = "buf.0"; char msg[] = "½Ð¿ï¾Ü¼È¦sÀÉ (0-9)[0]: "; char choice[2]; msg[19] = fp_buf[4]; do { if (!getdata(y, 0, msg, choice, sizeof(choice), DOECHO)) choice[0] = fp_buf[4]; } while (choice[0] < '0' || choice[0] > '9'); fp_buf[4] = choice[0]; return fp_buf; } static void read_tmpbuf(int n) { FILE *fp; char fp_tmpbuf[80]; char tmpfname[] = "buf.0"; const char *tmpf; char ans[4] = "y"; if (curr_buf->totaln >= EDIT_LINE_LIMIT) { vmsg("Àɮפw¶W¹L³Ì¤j­­¨î¡AµLªk¦AŪ¤J¼È¦sÀÉ¡C"); return; } if (0 <= n && n <= 9) { tmpfname[4] = '0' + n; tmpf = tmpfname; } else { tmpf = ask_tmpbuf(3); n = tmpf[4] - '0'; } setuserfile(fp_tmpbuf, tmpf); if (n != 0 && n != 5 && more(fp_tmpbuf, NA) != -1) getdata(b_lines - 1, 0, "½T©wŪ¤J¶Ü(Y/N)?[Y]", ans, sizeof(ans), LCECHO); if (*ans != 'n' && (fp = fopen(fp_tmpbuf, "r"))) { load_file(fp, -1); fclose(fp); } } static void write_tmpbuf(void) { FILE *fp; char fp_tmpbuf[80], ans[4]; textline_t *p; off_t sz = 0; setuserfile(fp_tmpbuf, ask_tmpbuf(3)); if (dashf(fp_tmpbuf)) { more(fp_tmpbuf, NA); getdata(b_lines - 1, 0, "¼È¦sÀɤw¦³¸ê®Æ (A)ªþ¥[ (W)Âмg (Q)¨ú®ø¡H[A] ", ans, sizeof(ans), LCECHO); if (ans[0] == 'q') return; } if (ans[0] != 'w') // 'a' { sz = dashs(fp_tmpbuf); if (sz > EDIT_SIZE_LIMIT) { vmsg("¼È¦sÀɤw¶W¹L¤j¤p­­¨î¡AµLªk¦Aªþ¥[¡C"); return; } } if ((fp = fopen(fp_tmpbuf, (ans[0] == 'w' ? "w" : "a+")))) { for (p = curr_buf->firstline; p; p = p->next) { if (p->next || p->data[0]) fprintf(fp, "%s\n", p->data); } fclose(fp); } } static void erase_tmpbuf(void) { char fp_tmpbuf[80]; char ans[4] = "n"; setuserfile(fp_tmpbuf, ask_tmpbuf(3)); if (more(fp_tmpbuf, NA) != -1) getdata(b_lines - 1, 0, "½T©w§R°£¶Ü(Y/N)?[N]", ans, sizeof(ans), LCECHO); if (*ans == 'y') unlink(fp_tmpbuf); } /** * ½s¿è¾¹¦Û°Ê³Æ¥÷ *(³Ì¦h³Æ¥÷ 512 ¦æ (?)) */ void auto_backup(void) { if (curr_buf == NULL) return; if (curr_buf->currline) { FILE *fp; textline_t *p, *v; char bakfile[PATHLEN]; int count = 0; curr_buf->currline = NULL; setuserfile(bakfile, fp_bak); if ((fp = fopen(bakfile, "w"))) { for (p = curr_buf->firstline; p != NULL && count < 512; p = v, count++) { v = p->next; fprintf(fp, "%s\n", p->data); free_line(p); } fclose(fp); } } } /** * ¨ú¦^½s¿è¾¹³Æ¥÷ */ void restore_backup(void) { char bakfile[80], buf[80]; setuserfile(bakfile, fp_bak); if (dashf(bakfile)) { vs_hdr("½s¿è¾¹¦Û°Ê´_­ì"); getdata(1, 0, "±z¦³¤@½g¤å³¹©|¥¼§¹¦¨¡A(S)¼g¤J¼È¦sÀÉ (Q)ºâ¤F¡H[S] ", buf, 4, LCECHO); if (buf[0] != 'q') { setuserfile(buf, ask_tmpbuf(3)); Rename(bakfile, buf); } else unlink(bakfile); } } /* ¤Þ¥Î¤å³¹ */ static int garbage_line(const char *str) { int qlevel = 0; while (*str == ':' || *str == '>') { if (*(++str) == ' ') str++; if (qlevel++ >= 1) return 1; } while (*str == ' ' || *str == '\t') str++; if (qlevel >= 1) { if (!strncmp(str, "¡° ", 3) || !strncmp(str, "==>", 3) || strstr(str, ") ´£¨ì:\n")) return 1; } return (*str == '\n'); } static void quote_strip_ansi_inline(unsigned char *is) { unsigned char *os = is; while (*is) { if(*is != ESC_CHR) *os++ = *is; else { is ++; if(*is == '*') { /* ptt prints, keep it as normal */ *os++ = '*'; *os++ = '*'; } else { /* normal ansi, strip them out. */ while (*is && ANSI_IN_ESCAPE(*is)) is++; } } is++; } *os = 0; } static void do_quote(void) { int op; char buf[512]; getdata(b_lines - 1, 0, "½Ð°Ý­n¤Þ¥Î­ì¤å¶Ü(Y/N/All/Repost)¡H[Y] ", buf, 3, LCECHO); op = buf[0]; if (op != 'n') { FILE *inf; if ((inf = fopen(quote_file, "r"))) { char *ptr; int indent_mode0 = curr_buf->indent_mode; fgets(buf, sizeof(buf), inf); if ((ptr = strrchr(buf, ')'))) ptr[1] = '\0'; else if ((ptr = strrchr(buf, '\n'))) ptr[0] = '\0'; if ((ptr = strchr(buf, ':'))) { char *str; while (*(++ptr) == ' '); /* ¶¶¤â²o¦Ï¡A¨ú±o author's address */ if ((curredit & EDIT_BOTH) && (str = strchr(quote_user, '.'))) { strcpy(++str, ptr); str = strchr(str, ' '); assert(str); str[0] = '\0'; } } else ptr = quote_user; curr_buf->indent_mode = 0; insert_string("¡° ¤Þ­z¡m"); insert_string(ptr); insert_string("¡n¤§»Ê¨¥¡G\n"); if (op != 'a') /* ¥h±¼ header */ while (fgets(buf, sizeof(buf), inf) && buf[0] != '\n'); /* FIXME by MH: ¦pªG header ¨ì¤º¤å¤¤¶¡¨S¦³ªÅ¦æ¤À¹j¡A·|³y¦¨ All ¥H¥~ªº¼Ò¦¡ ³£¤Þ¤£¨ì¤º¤å¡C */ if (op == 'a') while (fgets(buf, sizeof(buf), inf)) { insert_char(':'); insert_char(' '); quote_strip_ansi_inline((unsigned char *)buf); insert_string(buf); } else if (op == 'r') while (fgets(buf, sizeof(buf), inf)) { /* repost, keep anything */ // quote_strip_ansi_inline((unsigned char *)buf); insert_string(buf); } else { if (curredit & EDIT_LIST) /* ¥h±¼ mail list ¤§ header */ while (fgets(buf, sizeof(buf), inf) && (!strncmp(buf, "¡° ", 3))); while (fgets(buf, sizeof(buf), inf)) { if (!strcmp(buf, "--\n")) break; if (!garbage_line(buf)) { insert_char(':'); insert_char(' '); quote_strip_ansi_inline((unsigned char *)buf); insert_string(buf); } } } curr_buf->indent_mode = indent_mode0; fclose(inf); } } } /** * ¼f¬d user ¤Þ¨¥ªº¨Ï¥Î */ static int check_quote(void) { register textline_t *p = curr_buf->firstline; register char *str; int post_line; int included_line; post_line = included_line = 0; while (p) { if (!strcmp(str = p->data, "--")) break; if (str[1] == ' ' && ((str[0] == ':') || (str[0] == '>'))) included_line++; else { while (*str == ' ' || *str == '\t') str++; if (*str) post_line++; } p = p->next; } if ((included_line >> 2) > post_line) { move(4, 0); outs("¥»½g¤å³¹ªº¤Þ¨¥¤ñ¨Ò¶W¹L 80%¡A½Ð±z°µ¨Ç·Lªº­×¥¿¡G\n\n" ANSI_COLOR(1;33) "1) ¼W¥[¤@¨Ç¤å³¹ ©Î 2) §R°£¤£¥²­n¤§¤Þ¨¥" ANSI_RESET); { char ans[4]; getdata(12, 12, "(E)Ä~Äò½s¿è (W)±j¨î¼g¤J¡H[E] ", ans, sizeof(ans), LCECHO); if (ans[0] == 'w') return 0; } return 1; } return 0; } /* Àɮ׳B²z¡GŪÀÉ¡B¦sÀÉ¡B¼ÐÃD¡Bñ¦WÀÉ */ off_t loadsitesig(const char *fname); static void read_file(const char *fpath, int splitSig) { FILE *fp; off_t offSig = -1; if (splitSig) offSig = loadsitesig(fpath); if ((fp = fopen(fpath, "r")) == NULL) { int fd; if ((fd = creat(fpath, 0600)) >= 0) { close(fd); return; } assert(fd >= 0); abort_bbs(0); } load_file(fp, offSig); fclose(fp); } void write_header(FILE * fp, const char *mytitle) { assert(mytitle); if (curredit & EDIT_MAIL || curredit & EDIT_LIST) { fprintf(fp, "%s %s (%s)\n", str_author1, cuser.userid, cuser.nickname ); } else { const char *ptr = mytitle; struct { char author[IDLEN + 1]; char board[IDLEN + 1]; char title[66]; time4_t date; /* last post's date */ int number; /* post number */ } postlog; memset(&postlog, 0, sizeof(postlog)); strlcpy(postlog.author, cuser.userid, sizeof(postlog.author)); if (curr_buf) curr_buf->ifuseanony = 0; #ifdef HAVE_ANONYMOUS if (currbrdattr & BRD_ANONYMOUS) { int defanony = (currbrdattr & BRD_DEFAULTANONYMOUS); #if defined(PLAY_ANGEL) && defined(BN_ANGELPRAY) // dirty hack here... sorry if (HasUserPerm(PERM_ANGEL) && currboard && strcmp(currboard, BN_ANGELPRAY) == 0) { char mynick[IDLEN+1]; angel_load_my_fullnick(mynick, sizeof(mynick)); getdata_str(3, 0, "½Ð¿é¤J·Q¥Îªº¦W¦r(¿é¤J[r]¬°¯u¦W): ", real_name, sizeof(real_name), DOECHO, mynick); } else #endif // PLAY_ANGEL && BN_ANGELPRAY if (defanony) getdata(3, 0, "½Ð¿é¤J§A·Q¥ÎªºID¡A¤]¥iª½±µ«ö[Enter]¡A" "©Î¬O«ö[r]¥Î¯u¦W¡G", real_name, sizeof(real_name), DOECHO); else getdata(3, 0, "½Ð¿é¤J§A·Q¥ÎªºID¡A¤]¥iª½±µ«ö[Enter]¨Ï¥Î­ìID¡G", real_name, sizeof(real_name), DOECHO); if (!real_name[0] && defanony) { strlcpy(real_name, "Anonymous", sizeof(real_name)); strlcpy(postlog.author, real_name, sizeof(postlog.author)); if (curr_buf) curr_buf->ifuseanony = 1; } else { if (!strcmp("r", real_name) || (!defanony && !real_name[0])) strlcpy(postlog.author, cuser.userid, sizeof(postlog.author)); else { snprintf(postlog.author, sizeof(postlog.author), "%s.", real_name); if (curr_buf) curr_buf->ifuseanony = 1; } } } #endif strlcpy(postlog.board, currboard, sizeof(postlog.board)); if (!strncmp(ptr, str_reply, 4)) ptr += 4; strlcpy(postlog.title, ptr, sizeof(postlog.title)); postlog.date = now; postlog.number = 1; append_record(".post", (fileheader_t *) & postlog, sizeof(postlog)); #ifdef HAVE_ANONYMOUS if (currbrdattr & BRD_ANONYMOUS) { int defanony = (currbrdattr & BRD_DEFAULTANONYMOUS); fprintf(fp, "%s %s (%s) %s %s\n", str_author1, postlog.author, (((!strcmp(real_name, "r") && defanony) || (!real_name[0] && (!defanony))) ? cuser.nickname : "²q²q§Ú¬O½Ö ? ^o^"), local_article ? str_post2 : str_post1, currboard); } else { fprintf(fp, "%s %s (%s) %s %s\n", str_author1, cuser.userid, cuser.nickname, local_article ? str_post2 : str_post1, currboard); } #else /* HAVE_ANONYMOUS */ fprintf(fp, "%s %s (%s) %s %s\n", str_author1, cuser.userid, cuser.nickname, local_article ? str_post2 : str_post1, currboard); #endif /* HAVE_ANONYMOUS */ } fprintf(fp, "¼ÐÃD: %s\n®É¶¡: %s\n", mytitle, ctime4(&now)); } off_t loadsitesig(const char *fname) { int fd = 0; off_t sz = 0, ret = -1; char *start, *sp; sz = dashs(fname); if (sz < 1) return -1; fd = open(fname, O_RDONLY); if (fd < 0) return -1; start = (char*)mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0); if (start) { sp = start + sz - 4 - 1; // 4 = \n--\n while (sp > start) { if ((*sp == '\n' && strncmp(sp, "\n--\n", 4) == 0) || (*sp == '\r' && strncmp(sp, "\r--\r", 4) == 0) ) { size_t szSig = sz - (sp-start+1); ret = sp - start + 1; // allocate string curr_buf->sitesig_string = (char*) malloc (szSig + 1); if (curr_buf->sitesig_string) { memcpy(curr_buf->sitesig_string, sp+1, szSig); curr_buf->sitesig_string[szSig] = 0; } break; } sp --; } munmap(start, sz); } close(fd); return ret; } void addsignature(FILE * fp, int ifuseanony) { FILE *fs; int i; char buf[WRAPMARGIN + 1]; char fpath[STRLEN]; char ch; if (!strcmp(cuser.userid, STR_GUEST)) { fprintf(fp, "\n--\n¡° µo«H¯¸ :" BBSNAME "(" MYHOSTNAME ") \n¡» From: %s\n", FROMHOST); return; } if (!ifuseanony) { int browsing = 0; SigInfo si; memset(&si, 0, sizeof(si)); browse_sigs: showsignature(fpath, &i, &si); if (si.total > 0){ char msg[64]; ch = isdigit(cuser.signature) ? cuser.signature : 'x'; sprintf(msg, (browsing || (si.max > si.show_max)) ? "½Ð¿ï¾Üñ¦WÀÉ (1-9, 0=¤£¥[ n=½­¶ x=ÀH¾÷)[%c]: ": "½Ð¿ï¾Üñ¦WÀÉ (1-9, 0=¤£¥[ x=ÀH¾÷)[%c]: ", ch); getdata(0, 0, msg, buf, 4, LCECHO); if(buf[0] == 'n') { si.show_start = si.show_max + 1; if(si.show_start > si.max) si.show_start = 0; browsing = 1; goto browse_sigs; } if (!buf[0]) buf[0] = ch; if (isdigit((int)buf[0])) ch = buf[0]; else ch = '1' + random() % (si.max+1); cuser.signature = buf[0]; if (ch != '0') { fpath[i] = ch; do { if ((fs = fopen(fpath, "r"))) { fputs("\n--\n", fp); for (i = 0; i < MAX_SIGLINES && fgets(buf, sizeof(buf), fs); i++) fputs(buf, fp); fclose(fs); fpath[i] = ch; } else fpath[i] = '1' + (fpath[i] - '1' + 1) % (si.max+1); } while (!isdigit((int)buf[0]) && si.max > 0 && ch != fpath[i]); } } } #ifdef HAVE_ORIGIN #ifdef HAVE_ANONYMOUS if (ifuseanony) fprintf(fp, "\n--\n¡° µo«H¯¸: " BBSNAME "(" MYHOSTNAME ") \n¡» From: %s\n", "°Î¦W¤Ñ¨Ïªº®a"); else #endif { char temp[33]; strlcpy(temp, FROMHOST, sizeof(temp)); fprintf(fp, "\n--\n¡° µo«H¯¸: " BBSNAME "(" MYHOSTNAME ") \n¡» From: %s\n", temp); } #endif } #ifdef USE_POST_ENTROPY static int get_string_entropy(const char *s) { int ent = 0; while (*s) { char c = *s++; if (!isascii(c) || isalnum(c)) ent++; } return ent; } #endif #ifdef EXP_EDIT_UPLOAD static void upload_file(void); #endif // EXP_EDIT_UPLOAD // return EDIT_ABORTED if aborted // KEEP_EDITING if keep editing // 0 if write ok & exit static int write_file(const char *fpath, int saveheader, int *islocal, char mytitle[STRLEN], int upload, int chtitle, int *pentropy) { FILE *fp = NULL; textline_t *p; char ans[TTLEN], *msg; int aborted = 0, line = 0, checksum[3], sum = 0, po = 1; int entropy = 0; assert(!chtitle || mytitle); vs_hdr("Àɮ׳B²z"); move(1,0); #ifdef EDIT_UPLOAD_ALLOWALL upload = 1; #endif // EDIT_UPLOAD_ALLOWALL // common trail if (currstat == SMAIL) outs("[S]Àx¦s"); else if (local_article) outs("[L]¯¸¤º«H¥ó (S)Àx¦s"); else outs("[S]Àx¦s (L)¯¸¤º«H¥ó"); #ifdef EXP_EDIT_UPLOAD if (upload) outs(" (U)¤W¶Ç¸ê®Æ"); #endif // EXP_EDIT_UPLOAD if (chtitle) outs(" (T)§ï¼ÐÃD"); outs(" (A)©ñ±ó (E)Ä~Äò (R/W/D)Ū¼g§R¼È¦sÀÉ"); getdata(2, 0, "½T©w­nÀx¦sÀɮ׶ܡH ", ans, 2, LCECHO); // avoid lots pots if (ans[0] != 'a') sleep(1); switch (ans[0]) { case 'a': outs("¤å³¹" ANSI_COLOR(1) " ¨S¦³ " ANSI_RESET "¦s¤J"); aborted = EDIT_ABORTED; break; case 'e': return KEEP_EDITING; #ifdef EXP_EDIT_UPLOAD case 'u': if (upload) upload_file(); return KEEP_EDITING; #endif // EXP_EDIT_UPLOAD case 'r': read_tmpbuf(-1); return KEEP_EDITING; case 'w': write_tmpbuf(); return KEEP_EDITING; case 'd': erase_tmpbuf(); return KEEP_EDITING; case 't': if (!chtitle) return KEEP_EDITING; move(3, 0); prints("¼ÐÃD¡G%s", mytitle); strlcpy(ans, mytitle, sizeof(ans)); if (getdata_buf(4, 0, "·s¼ÐÃD¡G", ans, sizeof(ans), DOECHO)) strlcpy(mytitle, ans, STRLEN); return KEEP_EDITING; case 's': if (!HasUserPerm(PERM_LOGINOK)) { local_article = 1; // only report if local can be set. if (islocal) { mvouts(2, 0, "±z©|¥¼³q¹L¨­¥÷½T»{¡A¥u¯à Local Save¡C\n"); pressanykey(); } } else local_article = 0; break; case 'l': local_article = 1; } if (!aborted) { if (saveheader && !(curredit & EDIT_MAIL) && check_quote()) return KEEP_EDITING; assert(*fpath); if ((fp = fopen(fpath, "w")) == NULL) { assert(fp); abort_bbs(0); } if (saveheader) write_header(fp, mytitle); } if (!aborted) { for (p = curr_buf->firstline; p; p = p->next) { msg = p->data; if (p->next == NULL && !msg[0]) // ignore lastline is empty continue; trim(msg); line++; /* check crosspost */ if (currstat == POSTING && po ) { int msgsum = StringHash(msg); if (msgsum) { if (postrecord.last_bid != currbid && postrecord.checksum[po] == msgsum) { po++; if (po > 3) { postrecord.times++; postrecord.last_bid = currbid; po = 0; } } else po = 1; if (line >= curr_buf->totaln / 2 && sum < 3) { checksum[sum++] = msgsum; } } } #ifdef USE_POST_ENTROPY // calculate the real content of msg if (entropy < ENTROPY_MAX) entropy += get_string_entropy(msg); else #endif entropy = ENTROPY_MAX; // write the message body fprintf(fp, "%s\n", msg); } } curr_buf->currline = NULL; // what if currbid == 0? add currstat checking. if (currstat == POSTING && postrecord.times > MAX_CROSSNUM-1 && !is_hidden_board_friend(currbid, currutmp->uid)) anticrosspost(); if (po && sum == 3) { memcpy(&postrecord.checksum[1], checksum, sizeof(int) * 3); if(postrecord.last_bid != currbid) postrecord.times = 0; } *pentropy = entropy; if (aborted) return aborted; if (islocal) *islocal = local_article; if (curr_buf->sitesig_string) fprintf(fp, curr_buf->sitesig_string); if (currstat == POSTING || currstat == SMAIL) { addsignature(fp, curr_buf->ifuseanony); } else if (currstat == REEDIT) { #ifndef ALL_REEDIT_LOG // why force signature in SYSOP board? if(strcmp(currboard, BN_SYSOP) == 0) #endif { fprintf(fp, "¡° ½s¿è: %-15s ¨Ó¦Û: %-20s (%s)\n", cuser.userid, FROMHOST, Cdate_mdHM(&now)); } } fclose(fp); return 0; } static inline int has_block_selection(void) { return curr_buf->blockln >= 0; } /** * a block is continual lines of the article. */ /** * stop the block selection. */ static void block_cancel(void) { if (has_block_selection()) { curr_buf->blockln = -1; curr_buf->blockline = NULL; curr_buf->redraw_everything = YEA; } } static inline void setup_block_begin_end(textline_t **begin, textline_t **end) { if (curr_buf->currln >= curr_buf->blockln) { *begin = curr_buf->blockline; *end = curr_buf->currline; } else { *begin = curr_buf->currline; *end = curr_buf->blockline; } } #define BLOCK_TRUNCATE 0 #define BLOCK_APPEND 1 /** * save the selected block to file 'fname.' * mode: BLOCK_TRUNCATE truncate mode * BLOCK_APPEND append mode */ static void block_save_to_file(const char *fname, int mode) { textline_t *begin, *end; char fp_tmpbuf[80]; FILE *fp; if (!has_block_selection()) return; setup_block_begin_end(&begin, &end); setuserfile(fp_tmpbuf, fname); if ((fp = fopen(fp_tmpbuf, mode == BLOCK_APPEND ? "a+" : "w+"))) { textline_t *p; for (p = begin; p != end; p = p->next) fprintf(fp, "%s\n", p->data); fprintf(fp, "%s\n", end->data); fclose(fp); } } /** * delete selected block */ static void block_delete(void) { textline_t *begin, *end; textline_t *p; if (!has_block_selection()) return; setup_block_begin_end(&begin, &end); // the block region is (currln, block) or (blockln, currln). textline_t *pnext; int deleted_line_count = abs(curr_buf->currln - curr_buf->blockln) + 1; if (begin->prev) begin->prev->next = end->next; if (end->next) end->next->prev = begin->prev; if (curr_buf->currln > curr_buf->blockln) { // case (blockln, currln) curr_buf->currln = curr_buf->blockln; curr_buf->curr_window_line -= deleted_line_count - 1; } else { // case (currln, blockln) } curr_buf->totaln -= deleted_line_count; curr_buf->currline = end->next; if (curr_buf->currline == NULL) { curr_buf->currline = begin->prev; curr_buf->currln--; curr_buf->curr_window_line--; } if (curr_buf->currline == NULL) { assert(curr_buf->currln == -1); assert(curr_buf->totaln == -1); curr_buf->currline = alloc_line(WRAPMARGIN); curr_buf->currln++; curr_buf->totaln++; } else { curr_buf->currline = adjustline(curr_buf->currline, WRAPMARGIN); } curr_buf->oldcurrline = curr_buf->currline; // maintain special line pointer if (curr_buf->firstline == begin) curr_buf->firstline = curr_buf->currline; if (curr_buf->lastline == end) curr_buf->lastline = curr_buf->currline; if (curr_buf->curr_window_line <= 0) { curr_buf->curr_window_line = 0; curr_buf->top_of_win = curr_buf->currline; } // remove buffer end->next = NULL; for (p = begin; p; p = pnext) { pnext = p->next; free_line(p); deleted_line_count--; } assert(deleted_line_count == 0); curr_buf->currpnt = 0; block_cancel(); } static void block_cut(void) { if (!has_block_selection()) return; block_save_to_file("buf.0", BLOCK_TRUNCATE); block_delete(); } static void block_copy(void) { if (!has_block_selection()) return; block_save_to_file("buf.0", BLOCK_TRUNCATE); block_cancel(); } static void block_prompt(void) { char fp_tmpbuf[80]; char tmpfname[] = "buf.0"; char mode[2] = "w"; char choice[2]; move(b_lines - 1, 0); clrtoeol(); if (!getdata(b_lines - 1, 0, "§â°Ï¶ô²¾¦Ü¼È¦sÀÉ (0:Cut, 5:Copy, 6-9, q: Cancel)[0] ", choice, sizeof(choice), LCECHO)) choice[0] = '0'; if (choice[0] < '0' || choice[0] > '9') goto cancel_block; if (choice[0] == '0') { block_cut(); return; } else if (choice[0] == '5') { block_copy(); return; } tmpfname[4] = choice[0]; setuserfile(fp_tmpbuf, tmpfname); if (dashf(fp_tmpbuf)) { more(fp_tmpbuf, NA); getdata(b_lines - 1, 0, "¼È¦sÀɤw¦³¸ê®Æ (A)ªþ¥[ (W)Âмg (Q)¨ú®ø¡H[W] ", mode, sizeof(mode), LCECHO); if (mode[0] == 'q') goto cancel_block; else if (mode[0] != 'a') mode[0] = 'w'; } if (vans("§R°£°Ï¶ô(Y/N)?[N] ") != 'y') goto cancel_block; block_save_to_file(tmpfname, mode[0] == 'a' ? BLOCK_APPEND : BLOCK_TRUNCATE); cancel_block: block_cancel(); } static void block_select(void) { curr_buf->blockln = curr_buf->currln; curr_buf->blockline = curr_buf->currline; } ///////////////////////////////////////////////////////////////////////////// // Syntax Highlight #define PMORE_USE_ASCII_MOVIE // disable this if you don't enable ascii movie #define ENABLE_PMORE_ASCII_MOVIE_SYNTAX // disable if you don't want rich colour syntax #ifdef PMORE_USE_ASCII_MOVIE // pmore movie header support unsigned char * mf_movieFrameHeader(unsigned char *p, unsigned char *end); #endif // PMORE_USE_ASCII_MOVIE enum { EOATTR_NORMAL = 0x00, EOATTR_SELECTED = 0x01, // selected (reverse) EOATTR_MOVIECODE= 0x02, // pmore movie EOATTR_BBSLUA = 0x04, // BBS Lua (header) EOATTR_COMMENT = 0x08, // comment syntax }; static const char * const luaKeywords[] = { "and", "break", "do", "else", "elseif", "end", "for", "if", "in", "not", "or", "repeat","return","then","until","while", NULL }; static const char * const luaDataKeywords[] = { "false", "function", "local", "nil", "true", NULL }; static const char * const luaFunctions[] = { "assert", "print", "tonumber", "tostring", "type", NULL }; static const char * const luaMath[] = { "abs", "acos", "asin", "atan", "atan2", "ceil", "cos", "cosh", "deg", "exp", "floor", "fmod", "frexp", "ldexp", "log", "log10", "max", "min", "modf", "pi", "pow", "rad", "random", "randomseed", "sin", "sinh", "sqrt", "tan", "tanh", NULL }; static const char * const luaTable[] = { "concat", "insert", "maxn", "remove", "sort", NULL }; static const char * const luaString[] = { "byte", "char", "dump", "find", "format", "gmatch", "gsub", "len", "lower", "match", "rep", "reverse", "sub", "upper", NULL }; static const char * const luaBbs[] = { "ANSI_COLOR", "ANSI_RESET", "ESC", "addstr", "clear", "clock", "clrtobot", "clrtoeol", "color", "ctime", "getch","getdata", "getmaxyx", "getstr", "getyx", "interface", "kball", "kbhit", "kbreset", "move", "moverel", "now", "outs", "pause", "print", "rect", "refresh", "setattr", "sitename", "sleep", "strip_ansi", "time", "title", "userid", "usernick", NULL }; static const char * const luaToc[] = { "author", "date", "interface", "latestref", "notes", "title", "version", NULL }; static const char * const luaBit[] = { "arshift", "band", "bnot", "bor", "bxor", "cast", "lshift", "rshift", NULL }; static const char * const luaStore[] = { "USER", "GLOBAL", "iolimit", "limit", "load", "save", NULL }; static const char * const luaLibs[] = { "bbs", "bit", "math", "store", "string", "table", "toc", NULL }; static const char* const * const luaLibAPI[] = { luaBbs, luaBit, luaMath, luaStore, luaString, luaTable, luaToc, NULL }; int synLuaKeyword(const char *text, int n, char *wlen) { int i = 0; const char * const *tbl = NULL; if (*text >= 'A' && *text <= 'Z') { // normal identifier while (n-- > 0 && (isalnum(*text) || *text == '_')) { text++; (*wlen) ++; } return 0; } if (*text >= '0' && *text <= '9') { // digits while (n-- > 0 && (isdigit(*text) || *text == '.' || *text == 'x')) { text++; (*wlen) ++; } return 5; } if (*text == '#') { text++; (*wlen) ++; // length of identifier while (n-- > 0 && (isalnum(*text) || *text == '_')) { text++; (*wlen) ++; } return -2; } // ignore non-identifiers if (!(*text >= 'a' && *text <= 'z')) return 0; // 1st, try keywords for (i = 0; luaKeywords[i] && *text >= *luaKeywords[i]; i++) { int l = strlen(luaKeywords[i]); if (n < l) continue; if (isalnum(text[l])) continue; if (strncmp(text, luaKeywords[i], l) == 0) { *wlen = l; return 3; } } for (i = 0; luaDataKeywords[i] && *text >= *luaDataKeywords[i]; i++) { int l = strlen(luaDataKeywords[i]); if (n < l) continue; if (isalnum(text[l])) continue; if (strncmp(text, luaDataKeywords[i], l) == 0) { *wlen = l; return 2; } } for (i = 0; luaFunctions[i] && *text >= *luaFunctions[i]; i++) { int l = strlen(luaFunctions[i]); if (n < l) continue; if (isalnum(text[l])) continue; if (strncmp(text, luaFunctions[i], l) == 0) { *wlen = l; return 6; } } for (i = 0; luaLibs[i]; i++) { int l = strlen(luaLibs[i]); if (n < l) continue; if (text[l] != '.' && text[l] != ':') continue; if (strncmp(text, luaLibs[i], l) == 0) { *wlen = l+1; text += l; text ++; n -= l; n--; break; } } tbl = luaLibAPI[i]; if (!tbl) { // calcualte wlen while (n-- > 0 && (isalnum(*text) || *text == '_')) { text++; (*wlen) ++; } return 0; } for (i = 0; tbl[i]; i++) { int l = strlen(tbl[i]); if (n < l) continue; if (isalnum(text[l])) continue; if (strncmp(text, tbl[i], l) == 0) { *wlen += l; return 6; } } // luaLib. only return -6; } void syn_pmore_render(char *os, int len, char *buf) { // XXX buf should be same length as s. char *s = (char *)mf_movieFrameHeader((unsigned char*)os, (unsigned char*)os + len); char attr = 1; char iname = 0; char prefix = 0; memset(buf, 0, len); if (!len || !s) return; // render: frame header memset(buf, attr++, (s - os)); len -= s - os; buf += s - os; while (len-- > 0) { switch (*s++) { case 'P': case 'E': *buf++ = attr++; return; case 'S': *buf++ = attr++; continue; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': *buf++ = attr; while (len > 0 && isascii(*s) && (isalnum(*s) || *s == '.') ) { *buf++ = attr; len--; s++; } return; case '#': *buf++ = attr++; while (len > 0) { *buf++ = attr; if (*s == '#') attr++; len--; s++; } return; case ':': *buf++ = attr; while (len > 0 && isascii(*s) && (isalnum(*s) || *s == ':') ) { *buf++ = attr; len--; if (*s++ == ':') break; } attr++; continue; case 'I': case 'G': iname = 0; *buf++ = attr++; prefix = 1; while (len > 0 && ( (isascii(*s) && isalnum(*s)) || strchr("+-,:lpf", *s)) ) { if (prefix) { if (!strchr(":lpf", *s)) break; prefix = 0; } *buf++ = attr; if (*s == ',') { attr++; prefix = 1; } s++; len--; } attr++; return; case 'K': *buf++ = attr; if (*s != '#') return; *buf++ = attr; s++; len--; // # while (len >0) { *buf++ = attr; len--; if (*s++ == '#') break; } attr++; continue; default: // unknown return; } } } /** * Just like outs, but print out '*' instead of 27(decimal) in the given string. * * FIXME column could not start from 0 */ static void edit_outs_attr_n(const char *text, int n, int attr) { int column = 0; register unsigned char inAnsi = 0; register unsigned char ch; int doReset = 0; const char *reset = ANSI_RESET; // syntax attributes char fComment = 0, fSingleQuote = 0, fDoubleQuote = 0, fSquareQuote = 0, fWord = 0; // movie syntax rendering #ifdef ENABLE_PMORE_ASCII_MOVIE_SYNTAX char movie_attrs[WRAPMARGIN+10] = {0}; char *pmattr = movie_attrs, mattr = 0; #endif #ifdef COLORED_SELECTION if ((attr & EOATTR_SELECTED) && (attr & ~EOATTR_SELECTED)) { reset = ANSI_COLOR(0;7;36); doReset = 1; outs(reset); } else #endif // if not defined, color by priority - selection first if (attr & EOATTR_SELECTED) { reset = ANSI_COLOR(0;7); doReset = 1; outs(reset); } else if (attr & EOATTR_MOVIECODE) { reset = ANSI_COLOR(0;36); doReset = 1; outs(reset); #ifdef ENABLE_PMORE_ASCII_MOVIE_SYNTAX syn_pmore_render((char*)text, n, movie_attrs); #endif } else if (attr & EOATTR_BBSLUA) { reset = ANSI_COLOR(0;1;31); doReset = 1; outs(reset); } else if (attr & EOATTR_COMMENT) { reset = ANSI_COLOR(0;1;34); doReset = 1; outs(reset); } #ifdef DBCSAWARE /* 0 = N/A, 1 = leading byte printed, 2 = ansi in middle */ register unsigned char isDBCS = 0; #endif while ((ch = *text++) && (++column < t_columns) && n-- > 0) { #ifdef ENABLE_PMORE_ASCII_MOVIE_SYNTAX mattr = *pmattr++; #endif if(inAnsi == 1) { if(ch == ESC_CHR) outc('*'); else { outc(ch); if(!ANSI_IN_ESCAPE(ch)) { inAnsi = 0; outs(reset); } } } else if(ch == ESC_CHR) { inAnsi = 1; #ifdef DBCSAWARE if(isDBCS == 1) { isDBCS = 2; outs(ANSI_COLOR(1;33) "?"); outs(reset); } #endif outs(ANSI_COLOR(1) "*"); } else { #ifdef DBCSAWARE if(isDBCS == 1) isDBCS = 0; else if (isDBCS == 2) { /* ansi in middle. */ outs(ANSI_COLOR(0;33) "?"); outs(reset); isDBCS = 0; continue; } else if(IS_BIG5_HI(ch)) { isDBCS = 1; // peak next char if(n > 0 && *text == ESC_CHR) continue; } #endif // Lua Parser! if (!attr && curr_buf->synparser && !fComment) { // syntax highlight! if (fSquareQuote) { if (ch == ']' && n > 0 && *(text) == ']') { fSquareQuote = 0; doReset = 0; // directly print quotes outc(ch); outc(ch); text++, n--; outs(ANSI_RESET); continue; } } else if (fSingleQuote) { if (ch == '\'') { fSingleQuote = 0; doReset = 0; // directly print quotes outc(ch); outs(ANSI_RESET); continue; } } else if (fDoubleQuote) { if (ch == '"') { fDoubleQuote = 0; doReset = 0; // directly print quotes outc(ch); outs(ANSI_RESET); continue; } } else if (ch == '-' && n > 0 && *(text) == '-') { fComment = 1; doReset = 1; outs(ANSI_COLOR(0;1;34)); } else if (ch == '[' && n > 0 && *(text) == '[') { fSquareQuote = 1; doReset = 1; fWord = 0; outs(ANSI_COLOR(1;35)); } else if (ch == '\'' || ch == '"') { if (ch == '"') fDoubleQuote = 1; else fSingleQuote = 1; doReset = 1; fWord = 0; outs(ANSI_COLOR(1;35)); } else { // normal words if (fWord) { // inside a word. if (--fWord <= 0){ fWord = 0; doReset = 0; outc(ch); outs(ANSI_RESET); continue; } } else if (isalnum(tolower(ch)) || ch == '#') { char attr[] = ANSI_COLOR(0;1;37); int x = synLuaKeyword(text-1, n+1, &fWord); if (fWord > 0) fWord --; if (x != 0) { // sorry, fixed string here. // 7 = *[0;1;3? if (x<0) { attr[4] = '0'; x= -x; } attr[7] = '0' + x; prints(attr); doReset = 1; } if (!fWord) { outc(ch); outs(ANSI_RESET); doReset = 0; continue; } } } } outc(ch); #ifdef ENABLE_PMORE_ASCII_MOVIE_SYNTAX // pmore Movie Parser! if (attr & EOATTR_MOVIECODE) { // only render when attribute was changed. if (mattr != *pmattr) { if (*pmattr) { prints(ANSI_COLOR(1;3%d), 8 - ((mattr-1) % 7+1) ); } else { outs(ANSI_RESET); } } } #endif // ENABLE_PMORE_ASCII_MOVIE_SYNTAX } } // this must be ANSI_RESET, not "reset". if(inAnsi || doReset) outs(ANSI_RESET); } static void edit_outs_attr(const char *text, int attr) { edit_outs_attr_n(text, scr_cols, attr); } static void edit_ansi_outs_n(const char *str, int n, int attr) { char c; while (n-- > 0 && (c = *str++)) { if(c == ESC_CHR && *str == '*') { // ptt prints /* Because moving within ptt_prints is too hard * let's just display it as-is. */ outc('*'); } else { outc(c); } } } static void edit_ansi_outs(const char *str, int attr) { return edit_ansi_outs_n(str, strlen(str), attr); } static int detect_attr(const char *ps, size_t len) { int attr = 0; #ifdef PMORE_USE_ASCII_MOVIE if (mf_movieFrameHeader((unsigned char*)ps, (unsigned char*)ps+len)) attr |= EOATTR_MOVIECODE; #endif #ifdef USE_BBSLUA if (bbslua_isHeader(ps, ps + len)) { attr |= EOATTR_BBSLUA; if (!curr_buf->synparser) { curr_buf->synparser = 1; // if you need indent, toggle by hotkey. // enabling indent by default may cause trouble to copy pasters // curr_buf->indent_mode = 1; } } #endif return attr; } static inline void display_textline_internal(textline_t *p, int i) { short tmp; void (*output)(const char *, int) = edit_outs_attr; void (*output_n)(const char *, int, int)= edit_outs_attr_n; int attr = EOATTR_NORMAL; move(i, 0); clrtoeol(); if (!p) { outc('~'); outs(ANSI_CLRTOEND); return; } if (curr_buf->ansimode) { output = edit_ansi_outs; output_n = edit_ansi_outs_n; } tmp = curr_buf->currln - curr_buf->curr_window_line + i; // parse attribute of line // selected attribute? if (has_block_selection() && ( (curr_buf->blockln <= curr_buf->currln && curr_buf->blockln <= tmp && tmp <= curr_buf->currln) || (curr_buf->currln <= tmp && tmp <= curr_buf->blockln)) ) { // outs(ANSI_REVERSE); // remove me when EOATTR is ready... attr |= EOATTR_SELECTED; } attr |= detect_attr(p->data, p->len); #ifdef DBCSAWARE if(mbcs_mode && curr_buf->edit_margin > 0) { if(curr_buf->edit_margin >= p->len) { (*output)("", attr); } else { int newpnt = curr_buf->edit_margin; unsigned char *pdata = (unsigned char*) (&p->data[0] + curr_buf->edit_margin); if(mbcs_mode) newpnt = fix_cursor(p->data, newpnt, FC_LEFT); if(newpnt == curr_buf->edit_margin-1) { /* this should be always 'outs'? */ // (*output)(ANSI_COLOR(1) "<" ANSI_RESET); outs(ANSI_COLOR(1) "<" ANSI_RESET); pdata++; } (*output)((char*)pdata, attr); } } else #endif (*output)((curr_buf->edit_margin < p->len) ? &p->data[curr_buf->edit_margin] : "", attr); if (attr) outs(ANSI_RESET); // workaround poor terminal outs(ANSI_CLRTOEND); } static void refresh_window(void) { register textline_t *p; register int i; for (p = curr_buf->top_of_win, i = 0; i < visible_window_height(); i++) { display_textline_internal(p, i); if (p) p = p->next; } edit_msg(); } static void goto_line(int lino) { if (lino > 0 && lino <= curr_buf->totaln + 1) { textline_t *p; p = curr_buf->firstline; curr_buf->currln = lino - 1; while (--lino && p->next) p = p->next; if (p) curr_buf->currline = p; else { curr_buf->currln = curr_buf->totaln; curr_buf->currline = curr_buf->lastline; } curr_buf->currpnt = 0; edit_window_adjust_middle(); } curr_buf->redraw_everything = YEA; } static void prompt_goto_line(void) { char buf[10]; if (getdata(b_lines - 1, 0, "¸õ¦Ü²Ä´X¦æ:", buf, sizeof(buf), DOECHO)) goto_line(atoi(buf)); } /** * search string interactively. * @param mode 0: prompt * 1: forward * -1: backward */ static void search_str(int mode) { const int max_keyword = 65; char *str; char ans[4] = "n"; if (curr_buf->searched_string == NULL) { if (mode != 0) return; curr_buf->searched_string = (char *)malloc(max_keyword * sizeof(char)); curr_buf->searched_string[0] = 0; } str = curr_buf->searched_string; if (!mode) { if (getdata_buf(b_lines - 1, 0, "[·j´M]ÃöÁä¦r:", str, max_keyword, DOECHO)) if (*str) { if (getdata(b_lines - 1, 0, "°Ï¤À¤j¤p¼g(Y/N/Q)? [N] ", ans, sizeof(ans), LCECHO) && *ans == 'y') curr_buf->substr_fp = strstr; else curr_buf->substr_fp = strcasestr; } } if (*str && *ans != 'q') { textline_t *p; char *pos = NULL; int lino; bool found = false; if (mode >= 0) { for (lino = curr_buf->currln, p = curr_buf->currline; p; p = p->next, lino++) { int offset = (lino == curr_buf->currln ? MIN(curr_buf->currpnt + 1, curr_buf->currline->len) : 0); pos = (*curr_buf->substr_fp)(p->data + offset, str); if (pos) { found = true; break; } } } else { for (lino = curr_buf->currln, p = curr_buf->currline; p; p = p->prev, lino--) { pos = (*curr_buf->substr_fp)(p->data, str); if (pos && (lino != curr_buf->currln || pos - p->data < curr_buf->currpnt)) { found = true; break; } } } if (found) { /* move window */ curr_buf->currline = p; curr_buf->currln = lino; curr_buf->currpnt = pos - p->data; edit_window_adjust_middle(); } } if (!mode) curr_buf->redraw_everything = YEA; } /** * move the cursor from bracket to corresponding bracket. */ static void match_paren(void) { char *parens = "()[]{}"; char *ptype; textline_t *p; int lino; int i = 0; bool found = false; char findch; char quotech = '\0'; enum MatchState { MATCH_STATE_NORMAL, MATCH_STATE_C_COMMENT, MATCH_STATE_QUOTE }; enum MatchState state = MATCH_STATE_NORMAL; int dir; int nested = 0; char cursorch = curr_buf->currline->data[curr_buf->currpnt]; if (!(ptype = strchr(parens, cursorch))) return; dir = (ptype - parens) % 2 == 0 ? 1 : -1; findch = *(ptype + dir); p = curr_buf->currline; lino = curr_buf->currln; i = curr_buf->currpnt; while (p && !found) { // next position i += dir; while (p && i < 0) { p = p->prev; if (p) i = p->len - 1; lino--; } while (p && i >= p->len) { p = p->next; i = 0; lino++; } if (!p) break; assert(0 <= i && i < p->len); // match char switch (state) { case MATCH_STATE_NORMAL: if (nested == 0 && p->data[i] == findch) { found = true; break; } if (p->data[i] == cursorch) nested++; else if (p->data[i] == findch) nested--; if (p->data[i] == '\'' || p->data[i] == '"') { quotech = p->data[i]; state = MATCH_STATE_QUOTE; } else if ((i+dir) >= 0 && p->data[i] == '/' && p->data[i+dir] == '*') { state = MATCH_STATE_C_COMMENT; i += dir; } break; case MATCH_STATE_C_COMMENT: if ((i+dir) >= 0 && p->data[i] == '*' && p->data[i+dir] == '/') { state = MATCH_STATE_NORMAL; i += dir; } break; case MATCH_STATE_QUOTE: if (p->data[i] == quotech) { if (i==0 || p->data[i-1] != '\\') state = MATCH_STATE_NORMAL; } break; } } if (found) { int top = curr_buf->currln - curr_buf->curr_window_line; int bottom = top + visible_window_height() - 1; assert(p); curr_buf->currpnt = i; curr_buf->currline = p; curr_buf->curr_window_line += lino - curr_buf->currln; curr_buf->currln = lino; if (lino < top || lino > bottom) { edit_window_adjust_middle(); } } } static void currline_shift_left(void) { int currpnt0; if (curr_buf->currline->len <= 0) return; currpnt0 = curr_buf->currpnt; curr_buf->currpnt = 0; delete_char(); curr_buf->currpnt = (currpnt0 <= curr_buf->currline->len) ? currpnt0 : currpnt0 - 1; if (curr_buf->ansimode) curr_buf->currpnt = ansi2n(n2ansi(curr_buf->currpnt, curr_buf->currline), curr_buf->currline); } static void currline_shift_right(void) { int currpnt0; if (curr_buf->currline->len >= WRAPMARGIN - 1) return; currpnt0 = curr_buf->currpnt; curr_buf->currpnt = 0; insert_char(' '); curr_buf->currpnt = currpnt0; } static void cursor_to_next_word(void) { while (curr_buf->currpnt < curr_buf->currline->len && isalnum((int)curr_buf->currline->data[++curr_buf->currpnt])); while (curr_buf->currpnt < curr_buf->currline->len && isspace((int)curr_buf->currline->data[++curr_buf->currpnt])); } static void cursor_to_prev_word(void) { while (curr_buf->currpnt && isspace((int)curr_buf->currline->data[--curr_buf->currpnt])); while (curr_buf->currpnt && isalnum((int)curr_buf->currline->data[--curr_buf->currpnt])); if (curr_buf->currpnt > 0) curr_buf->currpnt++; } static void delete_current_word(void) { while (curr_buf->currpnt < curr_buf->currline->len) { delete_char(); if (!isalnum((int)curr_buf->currline->data[curr_buf->currpnt])) break; } while (curr_buf->currpnt < curr_buf->currline->len) { delete_char(); if (!isspace((int)curr_buf->currline->data[curr_buf->currpnt])) break; } } /** * transform every "*[" in given string to KEY_ESC "[" */ static void transform_to_color(char *line) { while (line[0] && line[1]) if (line[0] == '*' && line[1] == '[') { line[0] = KEY_ESC; line += 2; } else ++line; } static void block_color(void) { textline_t *begin, *end, *p; setup_block_begin_end(&begin, &end); p = begin; while (1) { assert(p); transform_to_color(p->data); if (p == end) break; else p = p->next; } block_cancel(); } /** * insert ansi code */ static void insert_ansi_code(void) { int ch = curr_buf->insert_mode; curr_buf->insert_mode = curr_buf->redraw_everything = YEA; if (!curr_buf->ansimode) insert_string(ANSI_RESET); else { char ans[4]; move(b_lines - 2, 55); outs(ANSI_COLOR(1;33;40) "B" ANSI_COLOR(41) "R" ANSI_COLOR(42) "G" ANSI_COLOR(43) "Y" ANSI_COLOR(44) "L" ANSI_COLOR(45) "P" ANSI_COLOR(46) "C" ANSI_COLOR(47) "W" ANSI_RESET); if (getdata(b_lines - 1, 0, "½Ð¿é¤J «G«×/«e´º/­I´º[¥¿±`¥Õ¦r¶Â©³][0wb]¡G", ans, sizeof(ans), LCECHO)) { const char t[] = "BRGYLPCW"; char color[15]; char *tmp, *apos = ans; int fg, bg; strcpy(color, ESC_STR "["); if (isdigit((int)*apos)) { sprintf(color,"%s%c", color, *(apos++)); if (*apos) strcat(color, ";"); } if (*apos) { if ((tmp = strchr(t, toupper(*(apos++))))) fg = tmp - t + 30; else fg = 37; sprintf(color, "%s%d", color, fg); } if (*apos) { if ((tmp = strchr(t, toupper(*(apos++))))) bg = tmp - t + 40; else bg = 40; sprintf(color, "%s;%d", color, bg); } strcat(color, "m"); insert_string(color); } else insert_string(ANSI_RESET); } curr_buf->insert_mode = ch; } static inline void phone_mode_switch(void) { if (curr_buf->phone_mode) curr_buf->phone_mode = 0; else { curr_buf->phone_mode = 1; if (!curr_buf->last_phone_mode) curr_buf->last_phone_mode = 2; } } /** * return coresponding phone char of given key c */ static const char* phone_char(char c) { if (curr_buf->last_phone_mode > 0 && curr_buf->last_phone_mode < 20) { if (tolower(c)<'a'||(tolower(c)-'a') >= strlen(BIG5[curr_buf->last_phone_mode - 1]) / 2) return 0; return BIG5[curr_buf->last_phone_mode - 1] + (tolower(c) - 'a') * 2; } else if (curr_buf->last_phone_mode >= 20) { if (c == '.') c = '/'; if (c < '/' || c > '9') return 0; return table[curr_buf->last_phone_mode - 20] + (c - '/') * 2; } return 0; } /** * When get the key for phone mode, handle it (e.g. edit_msg) and return the * key. Otherwise return 0. */ static inline char phone_mode_filter(char ch) { if (!curr_buf->phone_mode) return 0; switch (ch) { case 'z': case 'Z': if (curr_buf->last_phone_mode < 20) curr_buf->last_phone_mode = 20; else curr_buf->last_phone_mode = 2; edit_msg(); return ch; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (curr_buf->last_phone_mode < 20) { curr_buf->last_phone_mode = ch - '0' + 1; curr_buf->redraw_everything = YEA; return ch; } break; case '-': if (curr_buf->last_phone_mode < 20) { curr_buf->last_phone_mode = 11; curr_buf->redraw_everything = YEA; return ch; } break; case '=': if (curr_buf->last_phone_mode < 20) { curr_buf->last_phone_mode = 12; curr_buf->redraw_everything = YEA; return ch; } break; case '`': if (curr_buf->last_phone_mode < 20) { curr_buf->last_phone_mode = 13; curr_buf->redraw_everything = YEA; return ch; } break; case '/': if (curr_buf->last_phone_mode >= 20) { curr_buf->last_phone_mode += 4; if (curr_buf->last_phone_mode > 27) curr_buf->last_phone_mode -= 8; curr_buf->redraw_everything = YEA; return ch; } break; case '*': if (curr_buf->last_phone_mode >= 20) { curr_buf->last_phone_mode++; if ((curr_buf->last_phone_mode - 21) % 4 == 3) curr_buf->last_phone_mode -= 4; curr_buf->redraw_everything = YEA; return ch; } break; } return 0; } #ifdef EXP_EDIT_UPLOAD static void upload_file(void) { size_t szdata = 0; int c = 1; char promptmsg = 0; clear(); block_cancel(); vs_hdr("¤W¶Ç¤å¦rÀÉ®×"); move(3,0); outs("§Q¥Î¥»ªA°È±z¥i¥H¤W¶Ç¸û¤jªº¤å¦rÀÉ (¦ý¤£­p¤J½Z¶O)¡C\n" "\n" "¤W¶Ç´Á¶¡±z¥´ªº¦r¼È®É¤£·|¥X²{¦b¿Ã¹õ¤W¡A°£¤F Ctrl-U ·|³QÂà´«¬° ANSI \n" "±±¨î½Xªº ESC ¥~¡A¨ä¥¦¯S®íÁä¤@«ß¨S¦³§@¥Î¡C\n" "\n" "½Ð¦b±zªº¹q¸£¥»¾÷ºÝ½Æ»s¦n¤º®e«á¶K¤W§Y¥i¶}©l¶Ç°e¡C\n"); do { if (!num_in_buf()) { move(10, 0); clrtobot(); prints("\n\n¸ê®Æ±µ¦¬¤¤... %u ¦ì¤¸²Õ¡C\n", (unsigned int)szdata); outs(ANSI_COLOR(1) "¡»¥þ³¡§¹¦¨«á«ö¤U End ©Î ^X/^Q/^C §Y¥i¦^¨ì½s¿èµe­±¡C" ANSI_RESET "\n"); promptmsg = 0; } c = igetch(); if (c < 0x100 && isprint2(c)) { insert_char(c); szdata ++; } else if (c == Ctrl('U') || c == ESC_CHR) { insert_char(ESC_CHR); szdata ++; } else if (c == Ctrl('I')) { insert_tab(); szdata ++; } else if (c == KEY_ENTER) { split(curr_buf->currline, curr_buf->currpnt); curr_buf->oldcurrline = curr_buf->currline; szdata ++; promptmsg = 1; } if (!promptmsg) promptmsg = (szdata && szdata % 1024 == 0); // all other keys are ignored. } while (c != KEY_END && c != Ctrl('X') && c != Ctrl('C') && c != Ctrl('Q') && curr_buf->totaln <= EDIT_LINE_LIMIT && szdata <= EDIT_SIZE_LIMIT); move(12, 0); prints("¶Ç°eµ²§ô: ¦¬¨ì %u ¦ì¤¸²Õ¡C", (unsigned int)szdata); vmsgf("¦^¨ì½s¿èµe­±"); } #endif // EXP_EDIT_UPLOAD /** ½s¿è³B²z¡G¥Dµ{¦¡¡BÁä½L³B²z * @param title NULL, §_«hªø«× STRLEN * @return EDIT_ABORTED abort * >= 0 ½s¿è¿ú¼Æ * ¥Ñ©ó¦U³B³£¥H == EDIT_ABORTED §PÂ_, ­Y·Q¶Ç¦^¨ä¥L­t­È­nª`·N */ int vedit2(const char *fpath, int saveheader, int *islocal, char title[STRLEN], int flags) { char last = 0; /* the last key you press */ int ch, tmp; int mode0 = currutmp->mode; int destuid0 = currutmp->destuid; int money = 0, entropy = 0; int interval = 0; time4_t th = now; int count = 0, tin = 0, quoted = 0; char trans_buffer[256]; STATINC(STAT_VEDIT); currutmp->mode = EDITING; currutmp->destuid = currstat; #ifdef DBCSAWARE mbcs_mode = (cuser.uflag & DBCSAWARE_FLAG) ? 1 : 0; #endif enter_edit_buffer(); if (*fpath) { read_file(fpath, (flags & EDITFLAG_TEXTONLY) ? 1 : 0); } if (*quote_file) { do_quote(); *quote_file = '\0'; quoted = 1; } if( curr_buf->oldcurrline != curr_buf->firstline || curr_buf->currline != curr_buf->firstline) { /* we must adjust because cursor (currentline) moved. */ curr_buf->oldcurrline = curr_buf->currline = curr_buf->top_of_win = curr_buf->firstline= adjustline(curr_buf->firstline, WRAPMARGIN); } /* No matter you quote or not, just start the cursor from (0,0) */ curr_buf->currpnt = curr_buf->currln = curr_buf->curr_window_line = curr_buf->edit_margin = curr_buf->last_margin = 0; /* if quote, move to end of file. */ if(quoted) { /* maybe do this in future. */ } while (1) { edit_check_healthy(); if (curr_buf->redraw_everything || has_block_selection()) { refresh_window(); curr_buf->redraw_everything = NA; } if( curr_buf->oldcurrline != curr_buf->currline ){ curr_buf->oldcurrline = adjustline(curr_buf->oldcurrline, curr_buf->oldcurrline->len); curr_buf->oldcurrline = curr_buf->currline = adjustline(curr_buf->currline, WRAPMARGIN); } if (curr_buf->ansimode) ch = n2ansi(curr_buf->currpnt, curr_buf->currline); else ch = curr_buf->currpnt - curr_buf->edit_margin; move(curr_buf->curr_window_line, ch); ch = igetch(); /* jochang debug */ if ((interval = (now - th))) { th = now; if ((char)ch != last) { money++; last = (char)ch; } } if (interval && interval == tin) { // Ptt : +- 1 ¬í¤]ºâ count++; if(count>60) { money = 0; count = 0; /* log_file("etc/illegal_money", LOG_CREAT | LOG_VF, ANSI_COLOR(1;33;46) "%s " ANSI_COLOR(37;45) " ¥Î¾÷¾¹¤Hµoªí¤å³¹ " ANSI_COLOR(37) " %s" ANSI_RESET "\n", cuser.userid, Cdate(&now)); post_violatelaw(cuser.userid, BBSMNAME "¨t²Îĵ¹î", "¥Î¾÷¾¹¤Hµoªí¤å³¹", "±j¨îÂ÷¯¸"); abort_bbs(0); */ } } else if(interval){ count = 0; tin = interval; } #ifndef DBCSAWARE /* this is almost useless! */ if (curr_buf->raw_mode) { switch (ch) { case Ctrl('S'): case Ctrl('Q'): case Ctrl('T'): continue; } } #endif if (phone_mode_filter(ch)) continue; if (ch < 0x100 && isprint2(ch)) { const char *pstr; if(curr_buf->phone_mode && (pstr=phone_char(ch))) insert_dchar(pstr); else insert_char(ch); curr_buf->lastindent = -1; } else { if (ch == KEY_UP || ch == KEY_DOWN ){ if (curr_buf->lastindent == -1) curr_buf->lastindent = curr_buf->currpnt; } else curr_buf->lastindent = -1; if (ch == KEY_ESC) switch (KEY_ESC_arg) { case ',': ch = Ctrl(']'); break; case '.': ch = Ctrl('T'); break; case 'v': ch = KEY_PGUP; break; case 'a': case 'A': ch = Ctrl('V'); break; case 'X': ch = Ctrl('X'); break; case 'q': ch = Ctrl('Q'); break; case 'o': ch = Ctrl('O'); break; case 's': ch = Ctrl('S'); break; } switch (ch) { case KEY_F10: case Ctrl('X'): /* Save and exit */ block_cancel(); tmp = write_file(fpath, saveheader, islocal, title, (flags & EDITFLAG_UPLOAD) ? 1 : 0, (flags & EDITFLAG_ALLOWTITLE) ? 1 : 0, &entropy); if (tmp != KEEP_EDITING) { currutmp->mode = mode0; currutmp->destuid = destuid0; exit_edit_buffer(); // adjust final money money *= POST_MONEY_RATIO; // money or entropy? if (money > (entropy * ENTROPY_RATIO) && entropy >= 0) money = (entropy * ENTROPY_RATIO) + 1; if (!tmp) return money; else return tmp; } curr_buf->oldcurrline = curr_buf->currline; curr_buf->redraw_everything = YEA; break; case KEY_F5: prompt_goto_line(); curr_buf->redraw_everything = YEA; break; case KEY_F8: t_users(); curr_buf->redraw_everything = YEA; break; case Ctrl('W'): block_cut(); break; case Ctrl('Q'): /* Quit without saving */ grayout(0, b_lines-1, GRAYOUT_DARK); ch = vmsg("µ²§ô¦ý¤£Àx¦s [y/N]? "); if (ch == 'y' || ch == 'Y') { currutmp->mode = mode0; currutmp->destuid = destuid0; exit_edit_buffer(); return -1; } curr_buf->redraw_everything = YEA; break; case Ctrl('C'): insert_ansi_code(); break; case KEY_ESC: switch (KEY_ESC_arg) { case 'U': t_users(); curr_buf->redraw_everything = YEA; break; case 'i': t_idle(); curr_buf->redraw_everything = YEA; break; case 'n': search_str(1); break; case 'p': search_str(-1); break; case 'L': case 'J': prompt_goto_line(); curr_buf->redraw_everything = YEA; break; case ']': match_paren(); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': read_tmpbuf(KEY_ESC_arg - '0'); curr_buf->oldcurrline = curr_buf->currline; curr_buf->redraw_everything = YEA; break; case 'l': /* block delete */ case ' ': if (has_block_selection()) block_prompt(); else block_select(); break; case 'u': block_cancel(); break; case 'c': block_copy(); break; case 'y': curr_buf->oldcurrline = undelete_line(); if (curr_buf->oldcurrline == NULL) curr_buf->oldcurrline = curr_buf->currline; break; case 'R': #ifdef DBCSAWARE case 'r': mbcs_mode =! mbcs_mode; #endif curr_buf->raw_mode ^= 1; break; case 'I': curr_buf->indent_mode ^= 1; break; case 'j': currline_shift_left(); break; case 'k': currline_shift_right(); break; case 'f': cursor_to_next_word(); break; case 'b': cursor_to_prev_word(); break; case 'd': delete_current_word(); break; } break; case Ctrl('S'): case KEY_F3: search_str(0); break; case Ctrl('U'): insert_char(ESC_CHR); break; case Ctrl('V'): /* Toggle ANSI color */ curr_buf->ansimode ^= 1; if (curr_buf->ansimode && has_block_selection()) block_color(); clear(); curr_buf->redraw_everything = YEA; break; case Ctrl('I'): insert_tab(); break; case KEY_ENTER: block_cancel(); if (curr_buf->totaln >= EDIT_LINE_LIMIT) { vmsg("Àɮפw¶W¹L³Ì¤j­­¨î¡AµLªk¦A¼W¥[¦æ¼Æ¡C"); break; } #ifdef MAX_EDIT_LINE if(curr_buf->totaln == ((flags & EDITFLAG_ALLOWLARGE) ? MAX_EDIT_LINE_LARGE : MAX_EDIT_LINE)) { vmsg("¤w¨ì¹F³Ì¤j¦æ¼Æ­­¨î¡C"); break; } #endif split(curr_buf->currline, curr_buf->currpnt); curr_buf->oldcurrline = curr_buf->currline; break; case Ctrl('G'): { unsigned int currstat0 = currstat; int mode0 = currutmp->mode; setutmpmode(EDITEXP); a_menu("½s¿è»²§U¾¹", "etc/editexp", (HasUserPerm(PERM_SYSOP) ? SYSOP : NOBODY), 0, trans_buffer); currstat = currstat0; currutmp->mode = mode0; } if (trans_buffer[0]) { FILE *fp1; if ((fp1 = fopen(trans_buffer, "r"))) { int indent_mode0 = curr_buf->indent_mode; char buf[WRAPMARGIN + 2]; curr_buf->indent_mode = 0; while (fgets(buf, sizeof(buf), fp1)) { if (!strncmp(buf, "§@ªÌ:", 5) || !strncmp(buf, "¼ÐÃD:", 5) || !strncmp(buf, "®É¶¡:", 5)) continue; insert_string(buf); } fclose(fp1); curr_buf->indent_mode = indent_mode0; edit_window_adjust(); } } curr_buf->redraw_everything = YEA; break; case Ctrl('P'): phone_mode_switch(); curr_buf->redraw_everything = YEA; break; case KEY_F1: case Ctrl('Z'): /* Help */ more("etc/ve.hlp", YEA); curr_buf->redraw_everything = YEA; break; case Ctrl('L'): clear(); curr_buf->redraw_everything = YEA; break; case KEY_LEFT: if (curr_buf->currpnt) { if (curr_buf->ansimode) curr_buf->currpnt = n2ansi(curr_buf->currpnt, curr_buf->currline); curr_buf->currpnt--; if (curr_buf->ansimode) curr_buf->currpnt = ansi2n(curr_buf->currpnt, curr_buf->currline); #ifdef DBCSAWARE if(mbcs_mode) curr_buf->currpnt = fix_cursor(curr_buf->currline->data, curr_buf->currpnt, FC_LEFT); #endif } else if (curr_buf->currline->prev) { curr_buf->curr_window_line--; curr_buf->currln--; curr_buf->currline = curr_buf->currline->prev; curr_buf->currpnt = curr_buf->currline->len; } break; case KEY_RIGHT: if (curr_buf->currline->len != curr_buf->currpnt) { if (curr_buf->ansimode) curr_buf->currpnt = n2ansi(curr_buf->currpnt, curr_buf->currline); curr_buf->currpnt++; if (curr_buf->ansimode) curr_buf->currpnt = ansi2n(curr_buf->currpnt, curr_buf->currline); #ifdef DBCSAWARE if(mbcs_mode) curr_buf->currpnt = fix_cursor(curr_buf->currline->data, curr_buf->currpnt, FC_RIGHT); #endif } else if (curr_buf->currline->next) { curr_buf->currpnt = 0; curr_buf->curr_window_line++; curr_buf->currln++; curr_buf->currline = curr_buf->currline->next; } break; case KEY_UP: cursor_to_prev_line(); break; case KEY_DOWN: cursor_to_next_line(); break; case Ctrl('B'): case KEY_PGUP: curr_buf->top_of_win = back_line(curr_buf->top_of_win, visible_window_height() - 1, false); curr_buf->currline = back_line(curr_buf->currline, visible_window_height() - 1, true); curr_buf->curr_window_line = get_lineno_in_window(); if (curr_buf->currpnt > curr_buf->currline->len) curr_buf->currpnt = curr_buf->currline->len; curr_buf->redraw_everything = YEA; break; case Ctrl('F'): case KEY_PGDN: curr_buf->top_of_win = forward_line(curr_buf->top_of_win, visible_window_height() - 1, false); curr_buf->currline = forward_line(curr_buf->currline, visible_window_height() - 1, true); curr_buf->curr_window_line = get_lineno_in_window(); if (curr_buf->currpnt > curr_buf->currline->len) curr_buf->currpnt = curr_buf->currline->len; curr_buf->redraw_everything = YEA; break; case KEY_END: case Ctrl('E'): curr_buf->currpnt = curr_buf->currline->len; break; case Ctrl(']'): /* start of file */ curr_buf->currline = curr_buf->top_of_win = curr_buf->firstline; curr_buf->currpnt = curr_buf->currln = curr_buf->curr_window_line = 0; curr_buf->redraw_everything = YEA; break; case Ctrl('T'): /* tail of file */ curr_buf->top_of_win = back_line(curr_buf->lastline, visible_window_height() - 1, false); curr_buf->currline = curr_buf->lastline; curr_buf->curr_window_line = get_lineno_in_window(); curr_buf->currln = curr_buf->totaln; curr_buf->redraw_everything = YEA; curr_buf->currpnt = 0; break; case KEY_HOME: case Ctrl('A'): curr_buf->currpnt = 0; break; case Ctrl('O'): // better not use ^O - UNIX not sending. case KEY_INS: /* Toggle insert/overwrite */ curr_buf->insert_mode ^= 1; break; case KEY_BS: case KEY_BS2: /* backspace */ block_cancel(); if (curr_buf->ansimode) { curr_buf->ansimode = 0; clear(); curr_buf->redraw_everything = YEA; } else { if (curr_buf->currpnt == 0) { if (!curr_buf->currline->prev) break; curr_buf->curr_window_line--; curr_buf->currln--; curr_buf->currline = adjustline(curr_buf->currline, curr_buf->currline->len); curr_buf->currline = curr_buf->currline->prev; curr_buf->currline = adjustline(curr_buf->currline, WRAPMARGIN); curr_buf->oldcurrline = curr_buf->currline; curr_buf->currpnt = curr_buf->currline->len; curr_buf->redraw_everything = YEA; if (curr_buf->currline->next == curr_buf->top_of_win) { curr_buf->top_of_win = curr_buf->currline; curr_buf->curr_window_line = 0; } if (*next_non_space_char(curr_buf->currline->next->data) == '\0') { delete_line(curr_buf->currline->next, 0); break; } join(curr_buf->currline); break; } #ifndef DBCSAWARE curr_buf->currpnt--; delete_char(); #else { int newpnt = curr_buf->currpnt - 1; if(mbcs_mode) newpnt = fix_cursor(curr_buf->currline->data, newpnt, FC_LEFT); for(; curr_buf->currpnt > newpnt;) { curr_buf->currpnt --; delete_char(); } } #endif } break; case Ctrl('D'): case KEY_DEL: /* delete current character */ block_cancel(); if (curr_buf->currline->len == curr_buf->currpnt) { join(curr_buf->currline); curr_buf->redraw_everything = YEA; } else { #ifndef DBCSAWARE delete_char(); #else { int w = 1; if(mbcs_mode) w = mchar_len((unsigned char*)(curr_buf->currline->data + curr_buf->currpnt)); for(; w > 0; w --) delete_char(); } #endif if (curr_buf->ansimode) curr_buf->currpnt = ansi2n(n2ansi(curr_buf->currpnt, curr_buf->currline), curr_buf->currline); } break; case Ctrl('Y'): /* delete current line */ curr_buf->currpnt = 0; case Ctrl('K'): /* delete to end of line */ block_cancel(); if (ch == Ctrl('Y') || curr_buf->currline->len == 0) { textline_t *p = curr_buf->currline->next; if (!p) { p = curr_buf->currline->prev; if (!p) { curr_buf->currline->data[0] = 0; curr_buf->currline->len = 0; break; } if (curr_buf->curr_window_line > 0) { curr_buf->curr_window_line--; } curr_buf->currln--; } if (curr_buf->currline == curr_buf->top_of_win) curr_buf->top_of_win = p; delete_line(curr_buf->currline, 1); curr_buf->currline = p; curr_buf->redraw_everything = YEA; curr_buf->oldcurrline = curr_buf->currline = adjustline(curr_buf->currline, WRAPMARGIN); break; } else if (curr_buf->currline->len == curr_buf->currpnt) { join(curr_buf->currline); curr_buf->redraw_everything = YEA; break; } curr_buf->currline->len = curr_buf->currpnt; curr_buf->currline->data[curr_buf->currpnt] = '\0'; break; } if (curr_buf->currln < 0) curr_buf->currln = 0; edit_window_adjust(); #ifdef DBCSAWARE if(mbcs_mode) curr_buf->currpnt = fix_cursor(curr_buf->currline->data, curr_buf->currpnt, FC_LEFT); #endif } if (curr_buf->ansimode) tmp = n2ansi(curr_buf->currpnt, curr_buf->currline); else tmp = curr_buf->currpnt; if (tmp < t_columns - 1) curr_buf->edit_margin = 0; else curr_buf->edit_margin = tmp / (t_columns - 8) * (t_columns - 8); if (!curr_buf->redraw_everything) { if (curr_buf->edit_margin != curr_buf->last_margin) { curr_buf->last_margin = curr_buf->edit_margin; curr_buf->redraw_everything = YEA; } else { move(curr_buf->curr_window_line, 0); clrtoeol(); if (curr_buf->ansimode) outs(curr_buf->currline->data); else { int attr = EOATTR_NORMAL; attr |= detect_attr(curr_buf->currline->data, curr_buf->currline->len); edit_outs_attr(&curr_buf->currline->data[curr_buf->edit_margin], attr); } outs(ANSI_RESET ANSI_CLRTOEND); edit_msg(); } } else { curr_buf->last_margin = curr_buf->edit_margin; } } /* main event loop */ exit_edit_buffer(); } int vedit(const char *fpath, int saveheader, int *islocal, char title[STRLEN]) { assert(title); return vedit2(fpath, saveheader, islocal, title, EDITFLAG_ALLOWTITLE); } /** * ½s¿è¤@¯ëÀÉ®× («D¬ÝªO¤å³¹/«H¥ó). * * ¦]¦¹¤£·|¦³ header, title, local save µ¥°Ñ¼Æ. */ int veditfile(const char *fpath) { return vedit2(fpath, NA, NULL, NULL, 0); } /* vim:sw=4:nofoldenable */