1 /* See LICENSE for licence details. */
3 # define VERSION "0.4.0.beta4"
5 # define VERSION GIT_VERSION
11 #define _XOPEN_SOURCE 600
39 #include <sys/ioctl.h>
40 #include <sys/select.h>
43 #include <sys/types.h>
47 #include <X11/Xatom.h>
49 #include <X11/Xutil.h>
50 #include <X11/cursorfont.h>
51 #include <X11/keysym.h>
53 ////////////////////////////////////////////////////////////////////////////////
54 #include "libk8sterm/k8sterm.h"
57 ////////////////////////////////////////////////////////////////////////////////
61 ////////////////////////////////////////////////////////////////////////////////
62 #define USAGE_VERSION "k8sterm " VERSION "\n(c) 2010-2012 st engineers and Ketmar // Vampire Avalon\n"
64 "usage: sterm [options]\n" \
66 "-v show version and exit\n" \
67 "-h show this help and exit\n" \
69 "-t title set window title\n" \
70 "-c class set window class\n" \
71 "-w windowid embed in window id given id\n" \
72 "-T termname set TERM name\n" \
73 "-C config_file use custom config file\n" \
74 "-l langiconv use given locale\n" \
75 "-R stcmd run this INTERNAL k8sterm command\n" \
76 "-e command... run given command and pass all following args to it\n"
79 ////////////////////////////////////////////////////////////////////////////////
80 /* masks for key translation */
81 #define XK_NO_MOD (UINT_MAX)
82 #define XK_ANY_MOD (0)
85 ////////////////////////////////////////////////////////////////////////////////
86 // internal command line mode
94 // X11 window state flags
96 K8T_WIN_VISIBLE
= 0x01,
97 K8T_WIN_REDRAW
= 0x02,
98 K8T_WIN_FOCUSED
= 0x04
102 ////////////////////////////////////////////////////////////////////////////////
103 /* Purely graphic info */
108 Cursor cursor
; // text cursor
109 Cursor defcursor
; // 'default' cursor
110 Cursor lastcursor
; // last used cursor before blanking
116 int w
; /* window width */
117 int h
; /* window height */
118 int bufw
; /* pixmap width */
119 int bufh
; /* pixmap height */
120 int ch
; /* char height */
121 int cw
; /* char width */
122 char state
; /* focus, redraw, visible */
124 int tch
; /* tab text char height */
129 //struct timeval lastdraw;
133 /* Drawing Context */
135 uint32_t *ncol
; // normal colors
136 uint32_t *bcol
; // b/w colors
137 uint32_t *gcol
; // green colors
151 typedef struct K8TCmdLine K8TCmdLine
;
153 typedef void (*CmdLineExecFn
) (K8Term
*term
, K8TCmdLine
*cmdline
, int cancelled
);
156 int cmdMode
; // K8T_CMDMODE_xxx
157 char cmdline
[K8T_UTF_SIZ
*CMDLINE_SIZE
];
158 int cmdreslen
; // byte length of 'reserved' (read-only) part
159 int cmdofs
; // byte offset of the first visible UTF-8 char in cmdline
160 char cmdc
[K8T_UTF_SIZ
+1]; // buffer to collect UTF-8 char
161 int cmdcl
; // index in cmdc, used to collect UTF-8 chars
162 int cmdtabpos
; // # of bytes (not UTF-8 chars!) used in autocompletion or -1
163 const char *cmdcurtabc
; // current autocompleted command
164 CmdLineExecFn cmdexecfn
;
169 /* internal terminal data */
174 int waitkeypress
; /* child is dead, awaiting for keypress */
175 char *exitmsg
; /* message for waitkeypress */
188 #define K8T_DATA(_term) ((K8TermData *)(_term->udata))
191 ////////////////////////////////////////////////////////////////////////////////
193 #include "getticks.c"
196 ////////////////////////////////////////////////////////////////////////////////
201 ////////////////////////////////////////////////////////////////////////////////
202 static void executeCommands (const char *str
);
203 static const char *findCommandCompletion (const char *str
, int slen
, const char *prev
);
205 static void tdrawfatalmsg (K8Term
*term
, const char *msg
);
207 static void tcmdput (K8Term
*term
, K8TCmdLine
*cmdline
, const char *s
, int len
);
209 static void xclearunused (void);
210 static void xseturgency (int add
);
211 static void xfixsel (void);
212 static void xblankPointer (void);
213 static void xunblankPointer (void);
214 static void xdrawTabBar (void);
215 static void xdrawcmdline (K8Term
*term
, K8TCmdLine
*cmdline
, int scry
);
217 static void termCreateXPixmap (K8Term
*term
);
218 static void termSetCallbacks (K8Term
*term
);
219 static void termSetDefaults (K8Term
*term
);
221 static void getButtonInfo (K8Term
*term
, XEvent
*e
, int *b
, int *x
, int *y
);
223 static void fixWindowTitle (const K8Term
*t
);
225 static void scrollHistory (K8Term
*term
, int delta
);
227 static int termsDoIO (int xfd
);
228 //static void doTermWrFlush (K8Term *term);
231 ////////////////////////////////////////////////////////////////////////////////
232 static inline uint32_t getColor (int idx
) {
233 if (globalBW
&& (curterm
== NULL
|| !curterm
->blackandwhite
)) return dc
.clrs
[globalBW
][idx
];
234 if (curterm
!= NULL
) return dc
.clrs
[curterm
->blackandwhite
%3][idx
];
235 return dc
.clrs
[0][idx
];
239 ////////////////////////////////////////////////////////////////////////////////
240 #include "iniparse.c"
244 ////////////////////////////////////////////////////////////////////////////////
245 #include "termswitch.c"
249 ////////////////////////////////////////////////////////////////////////////////
250 #include "tcmdline.c"
251 #include "tfatalbox.c"
254 ////////////////////////////////////////////////////////////////////////////////
257 #include "x11drawtabs.c"
258 #include "x11drawcmdline.c"
261 ////////////////////////////////////////////////////////////////////////////////
262 #include "x11evtvis.c"
263 #include "x11evtkbd.c"
264 #include "x11evtsel.c"
265 #include "x11evtmouse.c"
268 ////////////////////////////////////////////////////////////////////////////////
272 ////////////////////////////////////////////////////////////////////////////////
274 static void xevtcbcmessage (XEvent
*e
) {
275 /* See xembed specs http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html */
276 if (e
->xclient
.message_type
== xw
.xembed
&& e
->xclient
.format
== 32) {
277 if (e
->xclient
.data
.l
[1] == XEMBED_FOCUS_IN
) {
278 xw
.state
|= K8T_WIN_FOCUSED
;
280 k8t_tmSendFocusEvent(curterm
, 1);
281 } else if (e
->xclient
.data
.l
[1] == XEMBED_FOCUS_OUT
) {
282 xw
.state
&= ~K8T_WIN_FOCUSED
;
283 k8t_tmSendFocusEvent(curterm
, 0);
285 k8t_drawCursor(curterm
, 0);
287 k8t_drawCopy(curterm
, 0, 0, curterm
->col
, curterm
->row
);
291 if (e
->xclient
.data
.l
[0] == XA_WM_DELETE_WINDOW
) {
292 closeRequestComes
= 1;
298 ////////////////////////////////////////////////////////////////////////////////
299 static K8TTimeMSec nscb_lastForcedRedraw
= 0; //FIXME: nasty global!
303 curterm->wantRedraw = 1;
304 curterm->lastDrawTime = 0;
306 static int afterNLSWCB (K8Term
*term
, int is_swap
) {
307 static int lines_20
= 0; //FIXME: nasty global!
309 /*FIXME: HACKERYHACK!*/
310 if (curterm
== term
&& opt_fastupdate
) {
314 if (!is_swap
&& lines_20
> 0) {
318 //if (is_swap) fprintf(stderr, "SWPALT: %d\n", K8T_ISSET(term, K8T_MODE_ALTSCREEN));
319 /* either not swapping or just swapped to alternate screen: longer delay */
322 if (K8T_ISSET(term
, K8T_MODE_ALTSCREEN
)) {
323 /* swapped to alternate screen */
327 /* swapped to normal screen */
333 xtime
= (K8T_ISSET(term
, K8T_MODE_ALTSCREEN
) ? 150 : 80);
338 if (tt
>= xtime
&& tt
-xtime
>= nscb_lastForcedRedraw
) {
339 k8t_tmWantRedraw(term
, 1); /* force it */
340 k8t_drawTerm(term
, 0);
343 K8TTimeMSec t1 = mclock_ticks();
344 if (t1-tt > 20) fprintf(stderr, "DRAWING TOO SLOW! %u\n", t1-tt);
347 nscb_lastForcedRedraw
= tt
;
348 curterm
->lastDrawTime
= tt
;
351 k8t_tmWantRedraw(term
, 0); /* shedule */
352 curterm
->lastDrawTime
= tt
;
354 curterm
->justSwapped
= 0;
362 static void afterNewLineCB (K8Term
*term
) {
363 afterNLSWCB(term
, 0);
367 static void afterScreenSwapCB (K8Term
*term
) {
368 //fprintf(stderr, "afterScreenSwapCB:%d; curterm==term:%d; opt_fastupdate:%d\n", K8T_ISSET(term, K8T_MODE_ALTSCREEN), curterm==term, opt_fastupdate);
369 afterNLSWCB(term
, 1);
373 static inline int last_draw_too_old (void) {
375 if (curterm
!= NULL
) {
376 if (curterm
->dead
|| !curterm
->wantRedraw
) return 0;
378 if (curterm
->justSwapped
) {
379 afterNLSWCB(curterm
, 1);
380 curterm
->justSwapped
= 0; /* repainter will fix it, but... */
386 (tt
-curterm
->lastDrawTime
>= opt_drawtimeout
||
387 tt
-lastDrawTime
>= (curterm
->justSwapped
? opt_swapdrawtimeout
: opt_maxdrawtimeout
));
390 return (tt
-lastDrawTime
>= opt_maxdrawtimeout
);
395 ////////////////////////////////////////////////////////////////////////////////
397 #include "childkiller.c"
398 static int childsigfd
= -1;
401 static void (*handler
[LASTEvent
])(XEvent
*) = {
402 [KeyPress
] = xevtcbkpress
,
403 [ClientMessage
] = xevtcbcmessage
,
404 [ConfigureNotify
] = xevtcbresise
,
405 [VisibilityNotify
] = xevtcbvisibility
,
406 [UnmapNotify
] = xevtcbunmap
,
407 [Expose
] = xevtcbexpose
,
408 [FocusIn
] = xevtcbfocus
,
409 [FocusOut
] = xevtcbfocus
,
410 [MotionNotify
] = xevtcbbmotion
,
411 [ButtonPress
] = xevtcbbpress
,
412 [ButtonRelease
] = xevtcbbrelease
,
413 [SelectionNotify
] = xevtcbselnotify
,
414 [SelectionRequest
] = xevtcbselrequest
,
415 [SelectionClear
] = xevtcbselclear
,
420 static void doTermWrFlush (K8Term *term) {
423 //struct timeval timeout;
425 if (term->dead || term->cmdfd < 0) {
426 // empty write buffer
427 term->wrbufpos = term->wrbufused = 0;
433 FD_SET(term->cmdfd, &rfd);
434 FD_SET(term->cmdfd, &wfd);
435 if (select(term->cmdfd+1, &rfd, &wfd, NULL, NULL) < 0) {
436 if (errno == EINTR) continue;
437 k8t_die("select failed: %s", strerror(errno));
440 if (FD_ISSET(term->cmdfd, &rfd)) {
441 int rd = k8t_ttyRead(term);
443 if (K8T_DATA(term)->waitkeypress && K8T_DATA(term)->exitmsg != NULL && rd < 0) {
444 // there will be no more data
448 tdrawfatalmsg(term, K8T_DATA(term)->exitmsg);
449 free(K8T_DATA(term)->exitmsg);
450 K8T_DATA(term)->exitmsg = NULL;
451 k8t_tmWantRedraw(term, 1);
452 // empty write buffer
453 term->wrbufpos = term->wrbufused = 0;
459 if (term->wrbufpos >= term->wrbufused) {
460 // empty write buffer
461 term->wrbufpos = term->wrbufused = 0;
466 if (K8T_DATA(term)->pid != 0 && FD_ISSET(term->cmdfd, &wfd)) {
467 k8t_ttySendWriteBuf(term);
474 static int termsDoIO (int xfd
) {
479 struct timeval timeout
;
485 if (xfd
>= 0) { FD_SET(xfd
, &rfd
); maxfd
= xfd
; } else { maxfd
= 0; }
486 //FD_SET(curterm->cmdfd, &rfd);
487 // have something to write?
488 for (int f
= 0; f
< term_count
; ++f
) {
489 K8Term
*t
= term_array
[f
];
491 if (!t
->dead
&& t
->cmdfd
>= 0) {
492 checkAndFixWindowTitle(t
, 0);
493 if (t
->cmdfd
> maxfd
) maxfd
= t
->cmdfd
;
494 FD_SET(t
->cmdfd
, &rfd
);
495 if (K8T_DATA(t
)->pid
!= 0 && t
->wrbufpos
< t
->wrbufused
) FD_SET(t
->cmdfd
, &wfd
);
500 timeout
.tv_usec
= (xfd
>= 0 ? (opt_drawtimeout
+2)*1000 : 0);
501 if (childsigfd
>= 0) {
502 if (childsigfd
> maxfd
) maxfd
= childsigfd
;
503 FD_SET(childsigfd
, &rfd
);
505 //fprintf(stderr, "before select...\n");
506 sres
= select(maxfd
+1, &rfd
, &wfd
, NULL
, &timeout
);
508 if (errno
== EINTR
) continue;
509 k8t_die("select failed: %s", strerror(errno
));
511 if (childsigfd
>= 0 && FD_ISSET(childsigfd
, &rfd
)) processDeadChildren(childsigfd
);
512 if (xfd
>= 0 && FD_ISSET(xfd
, &rfd
)) {
517 //fprintf(stderr, "after select...\n");
518 // process terminals i/o
519 for (int f
= 0; f
< term_count
; ++f
) {
520 K8Term
*t
= term_array
[f
];
522 if (!t
->dead
&& t
->cmdfd
>= 0) {
525 if (K8T_DATA(t
)->pid
!= 0 && FD_ISSET(t
->cmdfd
, &wfd
)) k8t_ttyFlushWriteBuf(t
);
526 if (FD_ISSET(t
->cmdfd
, &rfd
)) rd
= k8t_ttyRead(t
);
528 if (K8T_DATA(t
)->waitkeypress
&& K8T_DATA(t
)->exitmsg
!= NULL
&& rd
< 0) {
529 // there will be no more data
533 tdrawfatalmsg(t
, K8T_DATA(t
)->exitmsg
);
534 free(K8T_DATA(t
)->exitmsg
);
535 K8T_DATA(t
)->exitmsg
= NULL
;
536 k8t_tmWantRedraw(t
, 1);
546 static void run (void) {
547 //int stuff_to_print = 0;
548 int xfd
= XConnectionNumber(xw
.dpy
);
550 ptrLastMove
= mclock_ticks();
551 while (term_count
> 0) {
555 doX
= termsDoIO(xfd
);
558 if (term_count
== 0) exit(exitcode
);
561 if (curterm
!= NULL
&& curterm
->curblink
> 0) {
562 K8TTimeMSec tt
= mclock_ticks();
564 if (tt
-curterm
->lastBlinkTime
>= curterm
->curblink
) {
565 curterm
->lastBlinkTime
= tt
;
566 if ((xw
.state
&K8T_WIN_FOCUSED
) || curterm
->curblinkinactive
) {
567 curterm
->curbhidden
= (curterm
->curbhidden
? 0 : -1);
570 curterm
->curbhidden
= 0;
574 if (updateTabBar
) xdrawTabBar();
575 if (dodraw
|| last_draw_too_old()) k8t_drawTerm(curterm
, 0);
577 if (doX
|| XPending(xw
.dpy
)) {
578 while (XPending(xw
.dpy
)) {
579 XNextEvent(xw
.dpy
, &ev
);
581 //case VisibilityNotify:
589 ptrLastMove
= mclock_ticks();
593 if (XFilterEvent(&ev
, xw
.win
)) continue;
594 if (handler
[ev
.type
]) (handler
[ev
.type
])(&ev
);
598 if (curterm
!= NULL
) {
599 switch (closeRequestComes
) {
600 case 1: // just comes
601 if (opt_ignoreclose
== 0) {
602 tcmdlinehide(curterm
, &K8T_DATA(curterm
)->cmdline
);
603 tcmdlineinitex(curterm
, &K8T_DATA(curterm
)->cmdline
, "Do you really want to close sterm [N/y]? ");
604 K8T_DATA(curterm
)->cmdline
.cmdexecfn
= cmdline_closequeryexec
;
605 } else if (opt_ignoreclose
< 0) {
606 //FIXME: kill all clients?
609 closeRequestComes
= 0;
611 case 2: // ok, die now
612 //FIXME: kill all clients?
617 if (!ptrBlanked
&& opt_ptrblank
> 0 && mclock_ticks()-ptrLastMove
>= opt_ptrblank
) {
624 ////////////////////////////////////////////////////////////////////////////////
625 static void termSetDefaults (K8Term
*term
) {
626 term
->deffg
= defaultFG
;
627 term
->defbg
= defaultBG
;
628 term
->defboldfg
= defaultBoldFG
;
629 term
->defunderfg
= defaultUnderlineFG
;
631 term
->defcurfg
= defaultCursorFG
;
632 term
->defcurbg
= defaultCursorBG
;
633 term
->defcurinactivefg
= defaultCursorInactiveFG
;
634 term
->defcurinactivebg
= defaultCursorInactiveBG
;
636 term
->tabsize
= opt_tabsize
;
637 term
->wrapOnCtrlChars
= 0;
638 term
->historyLinesUsed
= 0;
642 static void termSetCallbacks (K8Term
*term
) {
643 K8TermData
*td
= calloc(1, sizeof(K8TermData
));
645 if (td
== NULL
) k8t_die("out of memory!");
648 term
->clockTicks
= clockTicksCB
;
649 term
->setLastDrawTime
= setLastDrawTimeCB
;
651 term
->isConversionNecessary
= isConversionNecessaryCB
;
652 term
->loc2utf
= loc2utfCB
;
653 term
->utf2loc
= utf2locCB
;
655 term
->isFocused
= isFocusedCB
;
656 term
->isVisible
= isVisibleCB
;
658 term
->drawSetFG
= drawSetFGCB
;
659 term
->drawSetBG
= drawSetBGCB
;
660 term
->drawRect
= drawRectCB
;
661 term
->drawFillRect
= drawFillRectCB
;
662 term
->drawCopyArea
= drawCopyAreaCB
;
663 term
->drawString
= drawStringCB
;
664 term
->drawOverlay
= drawOverlayCB
;
666 term
->fixSelection
= fixSelectionCB
;
667 term
->clearSelection
= clearSelectionCB
;
669 term
->titleChanged
= titleChangedCB
;
670 term
->doBell
= doBellCB
;
672 term
->isUnderOverlay
= isUnderOverlayCB
;
673 term
->clipToOverlay
= clipToOverlayCB
;
674 term
->addHistoryLine
= NULL
;
675 //term->doWriteBufferFlush = doTermWrFlush;
676 //term->doWriteBufferFlush = NULL;
677 term
->afterNewLine
= afterNewLineCB
;
678 term
->afterScreenSwap
= afterScreenSwapCB
; // the same
680 term
->unimap
= unimap
;
682 td
->lastpname
= strdup("");
683 td
->lastppath
= strdup("");
688 static void termCreateXPixmap (K8Term
*term
) {
689 int w
= K8T_MAX(1, term
->col
*xw
.cw
);
690 int h
= K8T_MAX(1, term
->row
*xw
.ch
);
693 newbuf
= XCreatePixmap(xw
.dpy
, xw
.win
, w
, h
, XDefaultDepth(xw
.dpy
, xw
.scr
));
696 //XCopyArea(xw.dpy, td->picbuf, newbuf, dc.gc, 0, 0, td->picbufw, td->picbufh, 0, 0);
697 XFreePixmap(xw
.dpy
, xw
.picscr
);
701 term
->drawSetFG(term
, term
->defbg
);
704 if (td->picalloced) {
705 if (td->picbufw > oldw) {
706 XFillRectangle(xw.dpy, newbuf, dc.gc, oldw, 0, td->picbufw-oldw, K8T_MIN(td->picbufh, oldh));
707 } else if (td->picbufw < oldw && xw.w > td->picbufw) {
708 XClearArea(xw.dpy, xw.win, td->picbufw, 0, xw.w-td->picbufh, K8T_MIN(td->picbufh, oldh), False);
711 if (td->picbufh > oldh) {
712 XFillRectangle(xw.dpy, newbuf, dc.gc, 0, oldh, td->picbufw, td->picbufh-oldh);
713 } else if (td->picbufh < oldh && xw.h > td->picbufh) {
714 XClearArea(xw.dpy, xw.win, 0, td->picbufh, xw.w, xw.h-td->picbufh, False);
717 XFillRectangle(xw.dpy, newbuf, dc.gc, 0, 0, td->picbufw, td->picbufh);
720 XFillRectangle(xw
.dpy
, newbuf
, dc
.gc
, 0, 0, w
, h
);
722 k8t_tmFullDirty(term
);
726 ////////////////////////////////////////////////////////////////////////////////
728 #include "commands.c"
731 ////////////////////////////////////////////////////////////////////////////////
732 #define PUSH_BACK(_c) (*ress)[dpos++] = (_c)
733 #define DECODE_TUPLE(tuple,bytes) \
734 for (tmp = bytes; tmp > 0; tmp--, tuple = (tuple & 0x00ffffff)<<8) \
735 PUSH_BACK((char)((tuple >> 24)&0xff))
737 // returns ress length
738 static int ascii85Decode (char **ress
, const char *srcs
/*, int start, int length*/) {
739 static uint32_t pow85
[5] = { 85*85*85*85UL, 85*85*85UL, 85*85UL, 85UL, 1UL };
740 const uint8_t *data
= (const uint8_t *)srcs
;
741 int len
= strlen(srcs
);
743 int count
= 0, c
= 0;
745 int start
= 0, length
= len
;
748 if (start
< 0) start
= 0; else { len
-= start
; data
+= start
; }
749 if (length
< 0 || len
< length
) length
= len
;
752 int xlen = 4*((length+4)/5);
753 kstringReserve(ress, xlen);
757 *ress
= (char *)calloc(1, len
+1);
758 for (int f
= length
; f
> 0; --f
, ++data
) {
760 if (c
<= ' ') continue; // skip blanks
762 case 'z': // zero tuple
764 //fprintf(stderr, "%s: z inside ascii85 5-tuple\n", file);
774 case '~': // '~>': end of sequence
775 if (f
< 1 || data
[1] != '>') { free(*ress
); return -2; } // error
776 if (count
> 0) { f
= -1; break; }
778 if (c
< '!' || c
> 'u') {
779 //fprintf(stderr, "%s: bad character in ascii85 region: %#o\n", file, c);
783 tuple
+= ((uint8_t)(c
-'!'))*pow85
[count
++];
785 DECODE_TUPLE(tuple
, 4);
792 // write last (possibly incomplete) tuple
794 tuple
+= pow85
[count
];
795 DECODE_TUPLE(tuple
, count
);
804 static void decodeBA (char *str
, int len
) {
807 for (int f
= 0; f
< len
; ++f
, ++str
) {
817 static void printEC (const char *txt
) {
821 if ((len
= ascii85Decode(&dest
, txt
)) >= 0) {
823 fprintf(stderr
, "%s\n", dest
);
829 static int isStr85Equ (const char *txt
, const char *str
) {
833 if (str
!= NULL
&& str
[0] && (len
= ascii85Decode(&dest
, txt
)) >= 0) {
834 if (len
> 0) res
= (strcmp(dest
+1, str
+1) == 0);
841 static int checkEGG (const char *str
) {
842 if (isStr85Equ("06:]JASq", str
) || isStr85Equ("0/i", str
)) {
844 "H8lZV&6)1>+AZ>m)Cf8;A1/cP+CnS)0OJ`X.QVcHA4^cc5r3=m1c%0D3&c263d?EV6@4&>"
845 "3DYQo;c-FcO+UJ;MOJ$TAYO@/FI]+B?C.L$>%:oPAmh:4Au)>AAU/H;ZakL2I!*!%J;(AK"
846 "NIR#5TXgZ6c'F1%^kml.JW5W8e;ql0V3fQUNfKpng6ppMf&ip-VOX@=jKl;#q\"DJ-_>jG"
847 "8#L;nm]!q;7c+hR6p;tVY#J8P$aTTK%c-OT?)<00,+q*8f&ff9a/+sbU,:`<H*[fk0o]7k"
848 "^l6nRkngc6Tl2Ngs!!P2I%KHG=7n*an'bsgn>!*8s7TLTC+^\\\"W+<=9^%Ol$1A1eR*Be"
849 "gqjEag:M0OnrC4FBY5@QZ&'HYYZ#EHs8t4$5]!22QoJ3`;-&=\\DteO$d6FBqT0E@:iu?N"
850 "a5ePUf^_uEEcjTDKfMpX/9]DFL8N-Ee;*8C5'WgbGortZuh1\\N0;/rJB6'(MSmYiS\"6+"
851 "<NK)KDV3e+Ad[@).W:%.dd'0h=!QUhghQaNNotIZGrpHr-YfEuUpsKW<^@qlZcdTDA!=?W"
852 "Yd+-^`'G8Or)<0-T&CT.i+:mJp(+/M/nLaVb#5$p2jR2<rl7\"XlngcN`mf,[4oK5JLr\\"
853 "m=X'(ue;'*1ik&/@T4*=j5t=<&/e/Q+2=((h`>>uN(#>&#i>2/ajK+=eib1coVe3'D)*75"
854 "m_h;28^M6p6*D854Jj<C^,Q8Wd\"O<)&L/=C$lUAQNN<=eTD:A6kn-=EItXSss.tAS&!;F"
855 "EsgpJTHIYNNnh'`kmX^[`*ELOHGcWbfPOT`J]A8P`=)AS;rYlR$\"-.RG440lK5:Dg?G'2"
856 "['dE=nEm1:k,,Se_=%-6Z*L^J[)EC"
860 if (isStr85Equ("04Jj?B)", str
)) {
862 "IPaSa(`c:T,o9Bq3\\)IY++?+!-S9%P0/OkjE&f$l.OmK'Ai2;ZHn[<,6od7^8;)po:HaP"
863 "m<'+&DRS:/1L7)IA7?WI$8WKTUB2tXg>Zb$.?\"@AIAu;)6B;2_PB5M?oBPDC.F)606Z$V"
864 "=ONd6/5P*LoWKTLQ,d@&;+Ru,\\ESY*rg!l1XrhpJ:\"WKWdOg?l;=RHE:uU9C?aotBqj]"
865 "=k8cZ`rp\"ZO=GjkfD#o]Z\\=6^]+Gf&-UFthT*hN"
869 if (isStr85Equ("04o69A7Tr", str
)) {
871 "Ag7d[&R#Ma9GVV5,S(D;De<T_+W).?,%4n+3cK=%4+0VN@6d\")E].np7l?8gF#cWF7SS_m"
872 "4@V\\nQ;h!WPD2h#@\\RY&G\\LKL=eTP<V-]U)BN^b.DffHkTPnFcCN4B;]8FCqI!p1@H*_"
873 "jHJ<%g']RG*MLqCrbP*XbNL=4D1R[;I(c*<FuesbWmSCF1jTW+rplg;9[S[7eDVl6YsjT"
881 ////////////////////////////////////////////////////////////////////////////////
882 int main (int argc
, char *argv
[]) {
883 char *configfile
= NULL
;
888 for (int f
= 1; f
< argc
; f
++) {
889 if (argv
[f
][0] == '-' && checkEGG(argv
[f
])) exit(1);
890 if (strcmp(argv
[f
], "-name") == 0) { ++f
; continue; }
891 if (strcmp(argv
[f
], "-into") == 0) { ++f
; continue; }
892 if (strcmp(argv
[f
], "-embed") == 0) { ++f
; continue; }
893 switch (argv
[f
][0] != '-' || argv
[f
][2] ? -1 : argv
[f
][1]) {
894 case 'e': f
= argc
+1; break;
905 opt_term
= strdup(argv
[f
]);
911 if (configfile
!= NULL
) free(configfile
);
912 configfile
= strdup(argv
[f
]);
916 if (++f
< argc
) cliLocale
= argv
[f
];
918 case 'S': // single-tab mode
922 fprintf(stderr
, "%s", USAGE_VERSION
);
926 fprintf(stderr
, "%s%s", USAGE_VERSION
, USAGE
);
931 initDefaultOptions();
932 if (configfile
== NULL
) {
933 static const char *systemcfg
[] = {"./.sterm.rc", "/etc/sterm.rc", "/etc/.sterm.rc", "/etc/sterm/sterm.rc", "/etc/sterm/.sterm.rc"};
934 const char *home
= getenv("HOME");
936 static const char *homecfg
[] = {"%s/.sterm.rc", "%s/.config/sterm.rc", "%s/.config/sterm/sterm.rc"};
937 for (size_t f
= 0; f
< sizeof(homecfg
)/sizeof(homecfg
[0]); ++f
) {
938 configfile
= SPrintf(homecfg
[f
], home
);
939 if (loadConfig(configfile
) == 0) goto cfgdone
;
944 for (size_t f
= 0; f
< sizeof(systemcfg
)/sizeof(systemcfg
[0]); ++f
) {
945 configfile
= SPrintf(systemcfg
[f
]);
946 if (loadConfig(configfile
) == 0) goto cfgdone
;
952 if (loadConfig(configfile
) < 0) k8t_die("config file '%s' not found!", configfile
);
955 if (configfile
!= NULL
) free(configfile
); configfile
= NULL
;
957 for (int f
= 1; f
< argc
; ++f
) {
958 if (strcmp(argv
[f
], "-name") == 0) { ++f
; continue; }
959 if (strcmp(argv
[f
], "-into") == 0 || strcmp(argv
[f
], "-embed") == 0) {
960 if (opt_embed
) free(opt_embed
);
961 opt_embed
= strdup(argv
[f
]);
964 switch (argv
[f
][0] != '-' || argv
[f
][2] ? -1 : argv
[f
][1]) {
968 opt_title
= strdup(argv
[f
]);
974 opt_class
= strdup(argv
[f
]);
979 if (opt_embed
) free(opt_embed
);
980 opt_embed
= strdup(argv
[f
]);
984 if (++f
< argc
) runcmd
= argv
[f
];
987 /* eat all remaining arguments */
988 if (++f
< argc
) opt_cmd
= &argv
[f
];
1000 setenv("TERM", opt_term
, 1);
1002 setlocale(LC_ALL
, "");
1004 childsigfd
= summonChildSaviour();
1005 if (childsigfd
< 0) k8t_die("can't summon dead children saviour!");
1008 curterm
= k8t_termalloc();
1009 k8t_tmInitialize(curterm
, 80, 25, opt_maxhistory
);
1010 // pixmap will be created in xinit()
1011 if (K8T_DATA(curterm
)->execcmd
!= NULL
) { free(K8T_DATA(curterm
)->execcmd
); K8T_DATA(curterm
)->execcmd
= NULL
; }
1012 if (k8t_ttyNew(curterm
) != 0) k8t_die("can't run process");
1015 k8t_selInit(curterm
);
1016 if (runcmd
!= NULL
) executeCommands(runcmd
);