diff options
Diffstat (limited to 'console/edit.c')
-rw-r--r-- | console/edit.c | 3886 |
1 files changed, 3886 insertions, 0 deletions
diff --git a/console/edit.c b/console/edit.c new file mode 100644 index 00000000..c6b3a544 --- /dev/null +++ b/console/edit.c @@ -0,0 +1,3886 @@ +/* $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. + * ³oÓª©¥»¦ü¥GÁÙ¦³¦a¤è¨S¦³×¥¿¦n, ¥i¯à¾ÉP segmentation fault . + * + * FIXME ¦b°Ï¶ô¼Ð°O¼Ò¦¡(blockln>=0)¤¤¹ï¼W§Rקï¥i¯à·|³y¦¨ blockln, blockpnt, + * and/or blockline ¿ù»~. ¬Æ¦Ü§â blockline ¬å±¼·| access ¨ì¤w³Q free ±¼ªº + * memory. ¥i¯àn§ï¦¨¼Ð°O¼Ò¦¡ readonly, ©Î¬O°µ¬Y¨Ç°Ê§@®É¦Û°Ê¨ú®ø¼Ð°O¼Ò¦¡ + * (blockln=-1) + * + * FIXME 20071201 piaip + * block selection ¤£ª¾¦ó®É¤wÅܬ° line level ¦Ó«D character level ¤F¡A + * ³o¼Ë¤]¤ñ¸û¦n¼g¡A©Ò¥H§â blockpnt ®³±¼§a¡I + * + * 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 + */ +#include "bbs.h" + +#define EDIT_SIZE_LIMIT (32768*1024) +#define EDIT_LINE_LIMIT (65530) // (1048576) + +#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¥Dn·|±q editor_internal_t ªº + * data structure ½Í°_¡C¹ï©ó¨C¤@Ó data member ªº¸Ô²Ó¥\¯à¡A½Ð¨£ sturcture + * ¤¤ªºµù¸Ñ¡C + * + * ¤å³¹ªº¤º®e (¥H¤UºÙ content) ¥H¡u¦æ¡v¬°³æ¦ì¡A¥Dn¥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¤ñ¸û¤£ª½Ä±ªº¦æ¬°¬O¡G°£«D´å¼Ð¦b¶}©l¿ï¨ú¸ò¥Ø«e¡]µ²§ô¡^ªº¦ì¸m + * ¬O¦P¤@Ó¡]¦¹®É³oÓ½d³ò¬O¿ï¨úªº½d³ò¡^¡A§_«h´N¬O±q¶}©l¨º¤@¦C¤@ª½¨ì¥Ø«e¡]µ²§ô¡^ + * ³o¤@¦C¡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 + * ®É¡An´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¦Ó¥BY¤£©¯¹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 <victor@csie.org> + * 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; /* total lines 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 insert_c; /* insert this character when shift something + in order to compensate the new space. */ + 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 fp_bak[] = "bak"; + +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 void +indigestion(int i) +{ + vmsgf("ÄY«¤º¶Ë (%d)\n", i); + u_exit("EDITOR FAILED"); + assert(0); + exit(0); +} + +static inline void +edit_buffer_constructor(editor_internal_t *buf) +{ + /* all unspecified columns are 0 */ + buf->blockln = -1; + buf->insert_c = ' '; + buf->insert_mode = 1; + buf->redraw_everything = 1; + buf->lastindent = -1; +} + +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) +{ + 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) +{ + 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(); + + move(b_lines, 0); + clrtoeol(); + outs( ANSI_COLOR(37;44) " ½s¿è¤å³¹ " + ANSI_COLOR(31;47) " (^Z/F1)" ANSI_COLOR(30) "»¡©ú " + ANSI_COLOR(31;47) "(^P/^G)" ANSI_COLOR(30) "´¡¤J²Å¸¹/¹Ï¤ù " + ANSI_COLOR(31) "(^X/^Q)" ANSI_COLOR(30) "Â÷¶}"); + + prints( "ùø%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); + outslr("", 78, ANSI_RESET, 0); +} + +/** + * 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) +{ + while (num-- > 0) { + register textline_t *item; + + if (pos && (item = pos->prev)) { + pos = item; + curr_buf->currln--; + } + else + break; + } + return pos; +} + +/* calculate if cursor is at bottom, scroll required? + * currently vedit does NOT handle if curr_window_line > b_lines, + * take care if you changed curr_window_line! + */ +static inline int +cursor_at_bottom_line(void) +{ + return curr_buf->curr_window_line == b_lines || + (curr_buf->phone_mode && curr_buf->curr_window_line == b_lines - 1); +} + + +/** + * 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) +{ + while (num-- > 0) { + register textline_t *item; + + if (pos && (item = pos->next)) { + pos = item; + 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 +window_scroll_down(void) +{ + curr_buf->curr_window_line = 0; + + if (!curr_buf->top_of_win->prev) + indigestion(6); + else { + curr_buf->top_of_win = curr_buf->top_of_win->prev; + rscroll(); + } +} + +static inline void +window_scroll_up(void) +{ + curr_buf->curr_window_line = b_lines - (curr_buf->phone_mode ? 2 : 1); + + if (unlikely(!curr_buf->top_of_win->next)) + indigestion(7); + else { + curr_buf->top_of_win = curr_buf->top_of_win->next; + if(curr_buf->phone_mode) + move(b_lines-1, 0); + else + move(b_lines, 0); + clrtoeol(); + scroll(); + } +} + +/** + * 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; + } + indigestion(13); + abort_bbs(0); + return NULL; +} + +/** + * 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«þ¨©¦^¨Ó. + * ¥Dn¬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; + +#ifdef deBUG + if(oldp->len > WRAPMARGIN || oldp->len < 0) { + kill(currpid, SIGSEGV); + } +#endif + + memcpy(tmpl, oldp, oldp->len + sizeof(textline_t)); + free_line(oldp); + + newp = alloc_line(len); + memcpy(newp, tmpl, len + sizeof(textline_t)); +#ifdef DEBUG + newp->mlength = len; +#endif + 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 */ + if (cursor_at_bottom_line()) + window_scroll_up(); + } 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; + + if (curr_buf->currpnt > i) { + indigestion(1); + return; + } + 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; + + 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) +{ + editor_internal_t tmp; + + if (!curr_buf->deleted_line) + return NULL; + + tmp.top_of_win = curr_buf->top_of_win; + tmp.indent_mode = curr_buf->indent_mode; + tmp.curr_window_line = curr_buf->curr_window_line; + + curr_buf->indent_mode = 0; + curr_buf->currpnt = 0; + curr_buf->currln++; + insert_string(curr_buf->deleted_line->data); + insert_string("\n"); + + curr_buf->top_of_win = tmp.top_of_win; + curr_buf->indent_mode = tmp.indent_mode; + curr_buf->curr_window_line = tmp.curr_window_line; + + assert(curr_buf->currline->prev); + 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; + + if (curr_buf->currline->prev == NULL) { + curr_buf->top_of_win = curr_buf->currline; + curr_buf->currln = 0; + } + 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+B1<WRAPMARGIN + * line: A1 A2 B1 + * next: B2 " " + * call join($next) + */ +static int +join(textline_t * line) +{ + register textline_t *n; + register int ovfl; + + if (!(n = line->next)) + 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); + if (line->len + line->next->len >= WRAPMARGIN) { + indigestion(0); + return YEA; + } + join(line); + n = line->next; + ovfl = n->len - 1; + if (ovfl >= 0 && ovfl < WRAPMARGIN - 2) { + s = &(n->data[ovfl]); + if (*s != ' ') { + strcpy(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)) { + if (unlikely(curr_buf->currpnt >= len)) { + indigestion(1); + return; + } + 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ÀÉ */ +char * +ask_tmpbuf(int y) +{ + static char fp_buf[10] = "buf.0"; + static char msg[] = "½Ð¿ï¾Ü¼È¦sÀÉ (0-9)[0]: "; + + msg[19] = fp_buf[4]; + do { + if (!getdata(y, 0, msg, fp_buf + 4, 4, DOECHO)) + fp_buf[4] = msg[19]; + } while (fp_buf[4] < '0' || fp_buf[4] > '9'); + return fp_buf; +} + +static void +read_tmpbuf(int n) +{ + FILE *fp; + char fp_tmpbuf[80]; + char tmpfname[] = "buf.0"; + 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); + while (curr_buf->curr_window_line >= b_lines) { + curr_buf->curr_window_line--; + curr_buf->top_of_win = curr_buf->top_of_win->next; + } + } +} + +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; + + 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); + } + curr_buf->currline = NULL; + } +} + +/** + * ¨ú¦^½s¿è¾¹³Æ¥÷ + */ +void +restore_backup(void) +{ + char bakfile[80], buf[80]; + + setuserfile(bakfile, fp_bak); + if (dashf(bakfile)) { + stand_title("½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; + } + indigestion(4); + abort_bbs(0); + } + load_file(fp, offSig); + fclose(fp); +} + +void +write_header(FILE * fp, char *mytitle) // FIXME unused +{ + + if (curredit & EDIT_MAIL || curredit & EDIT_LIST) { + fprintf(fp, "%s %s (%s)\n", str_author1, cuser.userid, + cuser.nickname + ); + } else { + 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 (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 */ + + } + mytitle[72] = '\0'; + 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 EXP_EDIT_UPLOAD +static void upload_file(void); +#endif // EXP_EDIT_UPLOAD + +static int +write_file(char *fpath, int saveheader, int *islocal, char *mytitle, int upload, int chtitle) +{ + struct tm *ptime; + FILE *fp = NULL; + textline_t *p, *v; + char ans[TTLEN], *msg; + int aborted = 0, line = 0, checksum[3], sum = 0, po = 1; + + stand_title("Àɮ׳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©wnÀx¦sÀɮ׶ܡH ", ans, 2, LCECHO); + + // avoid lots pots + sleep(1); + + switch (ans[0]) { + case 'a': + outs("¤å³¹" ANSI_COLOR(1) " ¨S¦³ " ANSI_RESET "¦s¤J"); + aborted = -1; + 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; + move(2, 0); + outs("±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; + + if (!(*fpath)) + setuserfile(fpath, "ve_XXXXXX"); + if ((fp = fopen(fpath, "w")) == NULL) { + indigestion(5); + abort_bbs(0); + } + if (saveheader) + write_header(fp, mytitle); + } + for (p = curr_buf->firstline; p; p = v) { + v = p->next; + if (!aborted) { + assert(fp); + msg = p->data; + if (v || msg[0]) { + 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; + } + } + } + fprintf(fp, "%s\n", msg); + } + } + free_line(p); + } + 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; + } + + 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, GLOBAL_SYSOP) == 0) +#endif + { + ptime = localtime4(&now); + fprintf(fp, + "¡° ½s¿è: %-15s ¨Ó¦Û: %-20s (%02d/%02d %02d:%02d)\n", + cuser.userid, fromhost, + ptime->tm_mon + 1, ptime->tm_mday, + ptime->tm_hour, ptime->tm_min); + } + } + + 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->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). + + if (curr_buf->currln > curr_buf->blockln) { + // case (blockln, currln) + // piaip 2007/1201 ¦b³o¸Ì즳 offset-by-one issue + // ¦pªG¤S¹J¨ì¡A½ÐÀˬd³oªþªñ¡C + curr_buf->curr_window_line -= (curr_buf->currln - curr_buf->blockln); + + if (curr_buf->curr_window_line <= 0) { + curr_buf->curr_window_line = 0; + if (end->next) + (curr_buf->top_of_win = end->next)->prev = begin->prev; + else + curr_buf->top_of_win = (curr_buf->lastline = begin->prev); + } + curr_buf->currln -= (curr_buf->currln - curr_buf->blockln); + } else { + // case (currln, blockln) + } + + // adjust buffer after delete + if (begin->prev) + begin->prev->next = end->next; + else if (end->next) + curr_buf->top_of_win = curr_buf->firstline = end->next; + else { + curr_buf->currline = curr_buf->top_of_win = curr_buf->firstline = curr_buf->lastline = alloc_line(WRAPMARGIN); + curr_buf->currln = curr_buf->curr_window_line = curr_buf->edit_margin = 0; + } + + // adjust current line + if (end->next) { + curr_buf->currline = end->next; + curr_buf->currline->prev = begin->prev; + } + else if (begin->prev) { + curr_buf->currline = (curr_buf->lastline = begin->prev); + curr_buf->currln--; + if (curr_buf->curr_window_line > 0) + curr_buf->curr_window_line--; + } + + // remove buffer + for (p = begin; p != end; curr_buf->totaln--) + free_line((p = p->next)->prev); + + free_line(end); + curr_buf->totaln--; + + curr_buf->currpnt = 0; +} + +static void +block_cut(void) +{ + if (!has_block_selection()) + return; + + block_save_to_file("buf.0", BLOCK_TRUNCATE); + block_delete(); + + curr_buf->blockln = -1; + curr_buf->redraw_everything = YEA; +} + +static void +block_copy(void) +{ + if (!has_block_selection()) + return; + + block_save_to_file("buf.0", BLOCK_TRUNCATE); + + curr_buf->blockln = -1; + curr_buf->redraw_everything = YEA; +} + +static void +block_prompt(void) +{ + char fp_tmpbuf[80]; + char tmpfname[] = "buf.0"; + char mode[2]; + + move(b_lines - 1, 0); + clrtoeol(); + + if (!getdata(b_lines - 1, 0, "§â°Ï¶ô²¾¦Ü¼È¦sÀÉ (0:Cut, 5:Copy, 6-9, q: Cancel)[0] ", tmpfname + 4, 4, LCECHO)) + tmpfname[4] = '0'; + + if (tmpfname[4] < '0' || tmpfname[4] > '9') + goto cancel_block; + + if (tmpfname[4] == '0') { + block_cut(); + return; + } + else if (tmpfname[4] == '5') { + block_copy(); + return; + } + + 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 (getans("§R°£°Ï¶ô(Y/N)?[N] ") != 'y') + goto cancel_block; + + block_save_to_file(tmpfname, mode[0] == 'a' ? BLOCK_APPEND : BLOCK_TRUNCATE); + +cancel_block: + curr_buf->blockln = -1; + curr_buf->redraw_everything = YEA; +} + +static void +block_select(void) +{ + curr_buf->blockln = curr_buf->currln; + curr_buf->blockline = curr_buf->currline; +} + +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 *luaKeywords[] = { + "and", "break", "do", "else", "elseif", + "end", "for", "if", "in", "not", "or", + "repeat","return","then","until","while", + NULL +}; + +static const char *luaDataKeywords[] = { + "false", "function", "local", "nil", "true", + NULL +}; + +static const char *luaFunctions[] = { + "assert", "print", "tonumber", "tostring", "type", + NULL +}; + +static const char *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 *luaTable[] = { + "concat", "insert", "maxn", "remove", "sort", + NULL +}; + +static const char *luaString[] = { + "byte", "char", "dump", "find", "format", "gmatch", "gsub", "len", + "lower", "match", "rep", "reverse", "sub", "upper", NULL +}; + +static const char *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 *luaToc[] = { + "author", "date", "interface", "latestref", + "notes", "title", "version", + NULL +}; + +static const char *luaBit[] = { + "arshift", "band", "bnot", "bor", "bxor", "cast", "lshift", "rshift", + NULL +}; + +static const char *luaStore[] = { + "USER", "GLOBAL", "iolimit", "limit", "load", "save", + NULL +}; + +static const char *luaLibs[] = { + "bbs", "bit", "math", "store", "string", "table", "toc", + NULL +}; +static const char**luaLibAPI[] = { + luaBbs, luaBit, luaMath, luaStore, luaString, luaTable, luaToc, + NULL +}; + +int synLuaKeyword(const char *text, int n, char *wlen) +{ + int i = 0; + const char **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; +} + +/** + * 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; + +#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); + } + 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) + { + 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); + } + } + + // 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); +} + +// old compatible API +void +edit_outs(const char *text) +{ + edit_outs_attr(text, 0); +} + +void +edit_outs_n(const char *text, int n) +{ + edit_outs_attr_n(text, n, 0); +} + + +#define PMORE_USE_ASCII_MOVIE // disable this if you don't enable ascii movie + +#ifdef PMORE_USE_ASCII_MOVIE +// pmore movie header support +unsigned char * + mf_movieFrameHeader(unsigned char *p, unsigned char *end); + +#endif // PMORE_USE_ASCII_MOVIE + +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_COLOR(7)); // 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 < b_lines; 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; + + /* move window */ + 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; + 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; +} + +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; + + if (mode >= 0) { + for (lino = curr_buf->currln, p = curr_buf->currline; p; p = p->next, lino++) + if ((pos = (*curr_buf->substr_fp)(p->data + (lino == curr_buf->currln ? curr_buf->currpnt + 1 : 0), + str)) && (lino != curr_buf->currln || + pos - p->data != curr_buf->currpnt)) + break; + } else { + for (lino = curr_buf->currln, p = curr_buf->currline; p; p = p->prev, lino--) + if ((pos = (*curr_buf->substr_fp)(p->data, str)) && + (lino != curr_buf->currln || pos - p->data != curr_buf->currpnt)) + break; + } + if (pos) { + /* move window */ + curr_buf->currline = p; + curr_buf->currln = lino; + curr_buf->currpnt = pos - p->data; + if (lino < middle_line()) { + curr_buf->top_of_win = curr_buf->firstline; + curr_buf->curr_window_line = curr_buf->currln; + } else { + int i; + + 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; + } + } + if (!mode) + curr_buf->redraw_everything = YEA; +} + +/** + * move the cursor from bracket to corresponding bracket. + */ +static void +match_paren(void) +{ + char *parens = "()[]{}"; + int type; + int parenum = 0; + char *ptype; + textline_t *p; + int lino; + int c, i = 0; + + if (!(ptype = strchr(parens, curr_buf->currline->data[curr_buf->currpnt]))) + return; + + type = (ptype - parens) / 2; + parenum = ((ptype - parens) % 2) ? -1 : 1; + + /* FIXME CRASH */ + /* FIXME refactoring */ + if (parenum > 0) { + for (lino = curr_buf->currln, p = curr_buf->currline; p; p = p->next, lino++) { + int len = strlen(p->data); + for (i = (lino == curr_buf->currln) ? curr_buf->currpnt + 1 : 0; i < len; i++) { + if (p->data[i] == '/' && p->data[++i] == '*') { + ++i; + while (1) { + while (i < len && + !(p->data[i] == '*' && p->data[i + 1] == '/')) { + i++; + } + if (i >= len && p->next) { + p = p->next; + len = strlen(p->data); + ++lino; + i = 0; + } else + break; + } + } else if ((c = p->data[i]) == '\'' || c == '"') { + while (1) { + while (i < len - 1) { + if (p->data[++i] == '\\' && (size_t)i < len - 2) + ++i; + else if (p->data[i] == c) + goto end_quote; + } + if ((size_t)i >= len - 1 && p->next) { + p = p->next; + len = strlen(p->data); + ++lino; + i = -1; + } else + break; + } + end_quote: + ; + } else if ((ptype = strchr(parens, p->data[i])) && + (ptype - parens) / 2 == type) { + if (!(parenum += ((ptype - parens) % 2) ? -1 : 1)) + goto p_outscan; + } + } + } + } else { + for (lino = curr_buf->currln, p = curr_buf->currline; p; p = p->prev, lino--) { + int len = strlen(p->data); + for (i = ((lino == curr_buf->currln) ? curr_buf->currpnt - 1 : len - 1); i >= 0; i--) { + if (p->data[i] == '/' && p->data[--i] == '*' && i > 0) { + --i; + while (1) { + while (i > 0 && + !(p->data[i] == '*' && p->data[i - 1] == '/')) { + i--; + } + if (i <= 0 && p->prev) { + p = p->prev; + len = strlen(p->data); + --lino; + i = len - 1; + } else + break; + } + } else if ((c = p->data[i]) == '\'' || c == '"') { + while (1) { + while (i > 0) + if (i > 1 && p->data[i - 2] == '\\') + i -= 2; + else if ((p->data[--i]) == c) + goto begin_quote; + if (i <= 0 && p->prev) { + p = p->prev; + len = strlen(p->data); + --lino; + i = len; + } else + break; + } +begin_quote: + ; + } else if ((ptype = strchr(parens, p->data[i])) && + (ptype - parens) / 2 == type) { + if (!(parenum += ((ptype - parens) % 2) ? -1 : 1)) + goto p_outscan; + } + } + } + } +p_outscan: + if (!parenum) { + int top = curr_buf->currln - curr_buf->curr_window_line; + int bottom = curr_buf->currln - curr_buf->curr_window_line + b_lines - 1; + + 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) { + if (lino < middle_line()) { + curr_buf->top_of_win = curr_buf->firstline; + curr_buf->curr_window_line = curr_buf->currln; + } else { + int i; + + 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; + } + } +} + +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) { + // FIXME CRASH p will be NULL here. + 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(reset_color); + 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(reset_color); + } + 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(); + stand_title("¤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 == '\r' || c == '\n') + { + 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 */ +int +vedit2(char *fpath, int saveheader, int *islocal, int flags) +{ + char last = 0; /* the last key you press */ + int ch, tmp; + + int mode0 = currutmp->mode; + int destuid0 = currutmp->destuid; + int money = 0; + int interval = 0; + time4_t th = now; + int count = 0, tin = 0, quoted = 0; + char trans_buffer[256]; + char mytitle[STRLEN]; + + STATINC(STAT_VEDIT); + currutmp->mode = EDITING; + currutmp->destuid = currstat; + + strlcpy(mytitle, save_title, sizeof(mytitle)); + +#ifdef DBCSAWARE + mbcs_mode = (cuser.uflag & DBCSAWARE_FLAG) ? 1 : 0; +#endif + + enter_edit_buffer(); + + curr_buf->oldcurrline = curr_buf->currline = curr_buf->top_of_win = + curr_buf->firstline = curr_buf->lastline = alloc_line(WRAPMARGIN); + + 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) { + 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); + +#if 0 // DEPRECATED, it's really not a well known expensive feature + if (!curr_buf->line_dirty && strcmp(editline, curr_buf->currline->data)) + strcpy(editline, curr_buf->currline->data); +#endif + + 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, ctime4(&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; +#if 0 // DEPRECATED, it's really not a well known expensive feature + case '-': + ch = Ctrl('_'); + break; +#endif + case 's': + ch = Ctrl('S'); + break; + } + + switch (ch) { + case KEY_F10: + case Ctrl('X'): /* Save and exit */ + tmp = write_file(fpath, saveheader, islocal, mytitle, + (flags & EDITFLAG_UPLOAD) ? 1 : 0, + (flags & EDITFLAG_ALLOWTITLE) ? 1 : 0); + if (tmp != KEEP_EDITING) { + strlcpy(save_title, mytitle, sizeof(save_title)); + save_title[STRLEN-1] = 0; + currutmp->mode = mode0; + currutmp->destuid = destuid0; + + exit_edit_buffer(); + 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(); + // curr_buf->oldcurrline is freed in block_cut, and currline is + // well adjusted now. This will avoid re-adjusting later. + // It's not a good implementation, try to find a better + // solution! + curr_buf->oldcurrline = curr_buf->currline; + 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(); + // curr_buf->oldcurrline is freed in block_cut, and currline is + // well adjusted now. This will avoid re-adjusting later. + // It's not a good implementation, try to find a better + // solution! + curr_buf->oldcurrline = curr_buf->currline; + } + 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 '\r': + case '\n': + 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; + setutmpmode(EDITEXP); + a_menu("½s¿è»²§U¾¹", "etc/editexp", + (HasUserPerm(PERM_SYSOP) ? SYSOP : NOBODY), + 0, + trans_buffer); + currstat = currstat0; + } + 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; + while (curr_buf->curr_window_line >= b_lines) { + curr_buf->curr_window_line--; + curr_buf->top_of_win = curr_buf->top_of_win->next; + } + } + } + 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: { + short tmp = curr_buf->currln; + curr_buf->top_of_win = back_line(curr_buf->top_of_win, t_lines - 2); + curr_buf->currln = tmp; + curr_buf->currline = back_line(curr_buf->currline, t_lines - 2); + 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: { + short tmp = curr_buf->currln; + curr_buf->top_of_win = forward_line(curr_buf->top_of_win, t_lines - 2); + curr_buf->currln = tmp; + curr_buf->currline = forward_line(curr_buf->currline, t_lines - 2); + 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, t_lines - 1); + 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 */ + if (has_block_selection() && curr_buf->insert_mode) { + char ans[4]; + + getdata(b_lines - 1, 0, + "°Ï¶ô·L½Õ¥k²¾´¡¤J¦r¤¸(¹w³]¬°ªÅ¥Õ¦r¤¸)", + ans, sizeof(ans), LCECHO); + curr_buf->insert_c = ans[0] ? ans[0] : ' '; + } + curr_buf->insert_mode ^= 1; + break; + case Ctrl('H'): + case '\177': /* 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->currline->len = curr_buf->currpnt = 0; + case Ctrl('K'): /* delete to end of line */ + block_cancel(); + if (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; + 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; + + if (curr_buf->curr_window_line < 0) + window_scroll_down(); + else if (cursor_at_bottom_line()) + window_scroll_up(); +#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(); + } + } /* redraw */ + } /* main event loop */ + + exit_edit_buffer(); +} + +int +vedit(char *fpath, int saveheader, int *islocal) +{ + return vedit2(fpath, saveheader, islocal, EDITFLAG_ALLOWTITLE); +} + +/* vim:sw=4:nofoldenable + */ |