PageRenderTime 55ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/misc.c

https://gitlab.com/Jehan_ZeMarmot/mrxvt
C | 612 lines | 383 code | 97 blank | 132 comment | 140 complexity | 239396ea477090c1cf1f23c1715d57b7 MD5 | raw file
  1. /*--------------------------------*-C-*---------------------------------*
  2. * File: misc.c
  3. *----------------------------------------------------------------------*
  4. *
  5. * All portions of code are copyright by their respective author/s.
  6. * Copyright (c) 1996 mj olesen <olesen@me.QueensU.CA>
  7. * Copyright (c) 1997,1998 Oezguer Kesim <kesim@math.fu-berlin.de>
  8. * Copyright (c) 1998-2000 Geoff Wing <gcw@pobox.com>
  9. * Copyright (c) 2004 Jingmin Zhou <jimmyzhou@users.sourceforge.net>
  10. *
  11. * This program is free software; you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation; either version 2 of the License, or
  14. * (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, write to the Free Software
  23. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24. *----------------------------------------------------------------------*/
  25. #include "../config.h"
  26. #include "rxvt.h"
  27. /* EXTPROTO */
  28. char*
  29. rxvt_r_basename(const char *str)
  30. {
  31. char* base = STRRCHR(str, '/');
  32. return (char *)(base ? base + 1 : str);
  33. }
  34. /*
  35. * check that the first characters of S1 match S2
  36. *
  37. * No Match
  38. * return: 0
  39. * Match
  40. * return: STRLEN (S2)
  41. */
  42. /* EXTPROTO */
  43. int
  44. rxvt_str_match(const char *s1, const char *s2)
  45. {
  46. if( IS_NULL(s1) || IS_NULL(s2))
  47. return 0;
  48. else
  49. {
  50. int n = STRLEN(s2);
  51. return ( (STRNCMP(s1, s2, n) == 0) ? n : 0 );
  52. }
  53. }
  54. /* EXTPROTO */
  55. const char*
  56. rxvt_str_skip_space(const char *str)
  57. {
  58. if (str)
  59. while (*str && isspace((int) *str))
  60. str++;
  61. return str;
  62. }
  63. /*
  64. * remove leading/trailing space and strip-off leading/trailing quotes.
  65. * in place.
  66. */
  67. /* EXTPROTO */
  68. char*
  69. rxvt_str_trim(char *str)
  70. {
  71. char *r, *s;
  72. int n;
  73. if (!str || !*str) /* shortcut */
  74. return str;
  75. /* skip leading spaces */
  76. for (s = str; *s && isspace((int) *s); s++)
  77. ;
  78. /* goto end of string */
  79. for (n = 0, r = s; *r++; n++)
  80. ;
  81. r -= 2;
  82. /* dump return */
  83. if (n > 0 && *r == '\n')
  84. n--, r--;
  85. /* backtrack along trailing spaces */
  86. for (; n > 0 && isspace((int) *r); r--, n--)
  87. ;
  88. /* skip matching leading/trailing quotes */
  89. if( *s == '"' && *r == '"' && n > 1 )
  90. {
  91. s++;
  92. n -= 2;
  93. }
  94. /* copy back over: forwards copy */
  95. for (r = str; n; n--)
  96. *r++ = *s++;
  97. *r = '\0';
  98. return str;
  99. }
  100. /*
  101. * in-place interpretation of string:
  102. *
  103. * backslash-escaped: "\a\b\E\e\n\r\t", "\octal"
  104. * Ctrl chars: ^@ .. ^_, ^?
  105. *
  106. * returns the converted string length
  107. */
  108. /* EXTPROTO */
  109. int
  110. rxvt_str_escaped(char *str)
  111. {
  112. char ch, *s, *d;
  113. int i, num;
  114. if (IS_NULL(str) || *str == '\0') return 0;
  115. d = s = str;
  116. /*
  117. * 2006-03-23 gi1242: Disabled, as the user has no 'easy' way to send
  118. * strings beginning with M-x to mrxvt.
  119. *
  120. * 2006-05-24 gi1242: With macros, the user has an 'easy' way to send
  121. * strings to mrxvt. However emacs users should use emacs macros do do such
  122. * things, and not require code bloat in mrxvt for these random features.
  123. * Besides, mrxvt users should use Vim anyway ... :)
  124. */
  125. #if 0
  126. if (*s == 'M' && s[1] == '-')
  127. {
  128. /* Emacs convenience, replace leading `M-..' with `\E..' */
  129. *d++ = C0_ESC;
  130. s += 2;
  131. if (toupper((int) *s) == 'X')
  132. /* append carriage-return for `M-xcommand' */
  133. for (*d++ = 'x', append = '\r', s++;
  134. isspace((int) *s);
  135. s++)
  136. ;
  137. }
  138. #endif
  139. for (; (ch = *s++);)
  140. {
  141. if (ch == '\\')
  142. {
  143. ch = *s++;
  144. if (ch >= '0' && ch <= '7') /* octal */
  145. {
  146. num = ch - '0';
  147. for (i = 0; i < 2; i++, s++)
  148. {
  149. ch = *s;
  150. if (ch < '0' || ch > '7')
  151. break;
  152. num = num * 8 + ch - '0';
  153. }
  154. ch = (char)num;
  155. }
  156. else if (ch == 'a')
  157. ch = C0_BEL; /* bell */
  158. else if (ch == 'b')
  159. ch = C0_BS; /* backspace */
  160. else if (ch == 'E' || ch == 'e')
  161. ch = C0_ESC; /* escape */
  162. else if (ch == 'n')
  163. ch = '\n'; /* newline */
  164. else if (ch == 'r')
  165. ch = '\r'; /* carriage-return */
  166. else if (ch == 't')
  167. ch = C0_HT; /* tab */
  168. else if (ch != '\\' && ch != '^' )
  169. *d++ = '\\'; /* Copy over backslash */
  170. }
  171. else if (ch == '^')
  172. {
  173. ch = *s++;
  174. if( ch == '?' )
  175. ch = 127;
  176. else if( toupper(ch) >= 'A' && toupper(ch) <= 'Z' )
  177. ch = toupper(ch) - '@';
  178. else
  179. *d++ = '^'; /* Copy over caret */
  180. }
  181. *d++ = ch;
  182. }
  183. #if 0 /* Users can terminate their own stinking strings */
  184. /* ESC] is an XTerm escape sequence, must be terminated */
  185. if (*str == '\0' && str[1] == C0_ESC && str[2] == ']')
  186. append = CHAR_ST;
  187. /* add trailing character as required */
  188. if (append && d[-1] != append)
  189. *d++ = append;
  190. #endif
  191. *d = '\0';
  192. return (d - str);
  193. }
  194. /*
  195. * % interpolate expand src, and copy into dst. Returns length of the
  196. * interpolated string.
  197. */
  198. /* EXTPROTO */
  199. int
  200. rxvt_percent_interpolate( rxvt_t *r, int page,
  201. const char *src, int len, char *dst, int maxLen )
  202. {
  203. int i=0, /* Unexpanded string index */
  204. j=0; /* Expanded string index */
  205. rxvt_dbgmsg ((DBG_DEBUG, DBG_MISC, "rxvt_percent_interpolate( r, %d, %s, %d, %s, %d )\n", page, src, len, "dst", maxLen));
  206. /* Must only get here for a valid tab */
  207. assert( page >=0 && page <= LTAB(r) );
  208. assert( NOT_NULL( PVTS( r, page ) ) && PVTS( r, page )->vts_idx != -1 );
  209. while( i < len-1 && j < maxLen-1 )
  210. {
  211. if( src[i] == '%' )
  212. {
  213. switch( src[++i] )
  214. {
  215. case '%':
  216. /* Copy % over */
  217. dst[j++] = src[i++];
  218. break;
  219. case 'n':
  220. /* Active tab number */
  221. j += snprintf( dst + j, maxLen - j, "%d", page+1 );
  222. i ++;
  223. break;
  224. case 't':
  225. /* Active tab title */
  226. j += snprintf( dst + j, maxLen -j,
  227. "%s", PVTS(r, page)->tab_title );
  228. i ++;
  229. break;
  230. case 'S':
  231. /* Exit status of dead processes */
  232. if( PVTS( r, page )->dead )
  233. j += snprintf( dst + j, maxLen - j, "%d",
  234. WEXITSTATUS( PVTS( r, page )->status ) );
  235. else
  236. dst[ j++ ] = src[ i ];
  237. i++;
  238. break;
  239. case 'N':
  240. /* Normal / abnormal exit status of dead processes */
  241. if( PVTS( r, page )->dead )
  242. j += snprintf( dst + j, maxLen - j, "%s",
  243. WIFEXITED( PVTS( r, page )->status )
  244. ? "normally" : "abnormally" );
  245. else
  246. dst[ j++ ] = src[ i ];
  247. i ++;
  248. break;
  249. case 's':
  250. /*
  251. * Selection. TODO Also paste selection if it is not
  252. * owned by mrxvt.
  253. */
  254. if( NOT_NULL( r->selection.text ) )
  255. j += snprintf( dst + j, maxLen -j,
  256. "%s", r->selection.text );
  257. i++;
  258. break;
  259. case 'p':
  260. /* Pid of process in current tab */
  261. j += snprintf( dst + j, maxLen - j, "%d",
  262. PVTS(r, page)->cmd_pid );
  263. i++;
  264. break;
  265. case 'P':
  266. /* PID of mrxvt */
  267. j += snprintf( dst + j, maxLen - j, "%d", getpid() );
  268. i++;
  269. break;
  270. case 'G':
  271. /* Global tab number (plus 1) */
  272. j += snprintf( dst + j, maxLen - j, "%d",
  273. PVTS( r, page )->globalTabNum + 1 );
  274. i ++;
  275. break;
  276. case 'T':
  277. /* # tabs created so far */
  278. j += snprintf( dst + j, maxLen - j, "%d",
  279. r->ntabs + 1 );
  280. i ++;
  281. break;
  282. default:
  283. rxvt_msg (DBG_ERROR, DBG_MISC, "Unrecognized flag %%%c in '%s'", src[i++], src );
  284. break;
  285. }
  286. }
  287. else
  288. dst[j++] = src[i++];
  289. }
  290. /* Copy last char over */
  291. if( i == len-1 && j < maxLen-1 )
  292. dst[j++] = src[i++];
  293. /* NULL terminate dst */
  294. if( j > maxLen - 1 )
  295. j = maxLen - 1;
  296. if( j == 0 || dst[j-1] )
  297. dst[j++] = 0;
  298. /* % expansion done. Copy the string and length over */
  299. return j;
  300. }
  301. /*
  302. * Split a comma-separated string into an array, stripping leading and
  303. * trailing spaces (and paired quotes) from each entry. Empty strings
  304. * are properly returned
  305. * Caller should free each entry and array when done
  306. */
  307. /* EXTPROTO */
  308. char**
  309. rxvt_splitcommastring(const char *cs)
  310. {
  311. int l, n, p;
  312. const char *s, *t;
  313. char **ret;
  314. if( IS_NULL(s = cs))
  315. s = "";
  316. for( n=1, t=s; *t; t++)
  317. if (*t == ',')
  318. n++;
  319. assert (n >= 0 && n+1 > 0); /* possible integer overflow? */
  320. ret = rxvt_malloc( (n + 1) * sizeof(char *) );
  321. ret[n] = NULL;
  322. for( l = 0, t = s; l < n; l++ )
  323. {
  324. for( ; *t && *t != ','; t++ );
  325. p = t - s;
  326. ret[l] = rxvt_malloc(p + 1);
  327. STRNCPY(ret[l], s, p);
  328. ret[l][p] = '\0';
  329. rxvt_str_trim(ret[l]);
  330. s = ++t;
  331. }
  332. return ret;
  333. }
  334. /*----------------------------------------------------------------------*
  335. * file searching
  336. */
  337. #if defined (BACKGROUND_IMAGE) || defined (HAVE_MENUBAR)
  338. /*
  339. * search for FILE in the current working directory, and within the
  340. * colon-delimited PATHLIST, adding the file extension EXT if required.
  341. *
  342. * FILE is either semi-colon or zero terminated
  343. */
  344. /* INTPROTO */
  345. char *
  346. rxvt_File_search_path(const char *pathlist, const char *file, const char *ext)
  347. {
  348. int maxpath, len;
  349. const char *p, *path;
  350. char name[256];
  351. if (!access(file, R_OK)) /* found (plain name) in current directory */
  352. return STRDUP(file);
  353. /* semi-colon delimited */
  354. if ((p = STRCHR(file, ';'))) len = (p - file);
  355. else len = STRLEN(file);
  356. #ifdef DEBUG
  357. getcwd(name, sizeof(name));
  358. rxvt_dbgmsg ((DBG_VERBOSE, DBG_MISC, "pwd: \"%s\"\n", name));
  359. rxvt_dbgmsg ((DBG_VERBOSE, DBG_MISC, "find: \"%.*s\"\n", len, file));
  360. #endif
  361. /* leave room for an extra '/' and trailing '\0' */
  362. maxpath = sizeof(name) - (len + (ext ? STRLEN(ext) : 0) + 2);
  363. if (maxpath <= 0)
  364. return NULL;
  365. /* check if we can find it now */
  366. STRNCPY(name, file, len);
  367. name[len] = '\0';
  368. if (!access(name, R_OK))
  369. return STRDUP(name);
  370. if (ext)
  371. {
  372. STRCAT(name, ext);
  373. if (!access(name, R_OK))
  374. return STRDUP(name);
  375. }
  376. for (path = pathlist; NOT_NULL(path) && *path != '\0'; path = p)
  377. {
  378. int n;
  379. /* colon delimited */
  380. if (IS_NULL(p = STRCHR(path, ':')))
  381. p = STRCHR(path, '\0');
  382. n = (p - path);
  383. if (*p != '\0') p++;
  384. if (n > 0 && n <= maxpath)
  385. {
  386. STRNCPY(name, path, n);
  387. if (name[n - 1] != '/') name[n++] = '/';
  388. name[n] = '\0';
  389. STRNCAT(name, file, len);
  390. if (!access(name, R_OK))
  391. return STRDUP(name);
  392. if (ext)
  393. {
  394. STRCAT(name, ext);
  395. if (!access(name, R_OK))
  396. return STRDUP(name);
  397. }
  398. }
  399. }
  400. return NULL;
  401. }
  402. /* EXTPROTO */
  403. char *
  404. rxvt_File_find(const char *file, const char *ext, const char *path)
  405. {
  406. char *f;
  407. if (IS_NULL(file) || *file == '\0')
  408. return NULL;
  409. do
  410. {
  411. char *envpath;
  412. if ((f = rxvt_File_search_path(path, file, ext))) break;
  413. /*
  414. * Failed to get the file from arg path. Try getting it from the env
  415. * variable PATH_ENV.
  416. */
  417. rxvt_dbgmsg ((DBG_DEBUG, DBG_MISC, "Searching for %s from env %s...\n", file, PATH_ENV));
  418. envpath = getenv( PATH_ENV);
  419. if( envpath)
  420. if(( f = rxvt_File_search_path( envpath, file, ext) )) break;
  421. /*
  422. * Check in ~/.mrxvt
  423. */
  424. rxvt_dbgmsg ((DBG_DEBUG, DBG_MISC, "Searching for %s in ~/.mrxvt\n", file));
  425. envpath = getenv("HOME");
  426. if( envpath )
  427. {
  428. char *homepath;
  429. homepath = rxvt_malloc( STRLEN(envpath) + STRLEN(PACKAGE_NAME) + 3);
  430. sprintf( homepath, "%s/.%s", envpath, PACKAGE_NAME);
  431. f = rxvt_File_search_path( homepath, file, ext);
  432. rxvt_free( homepath);
  433. }
  434. if( f ) break;
  435. /*
  436. * Last resort: Try a compiled in default.
  437. */
  438. rxvt_dbgmsg ((DBG_DEBUG, DBG_MISC, "Searching for %s in %s\n", file, PKG_CONF_DIR));
  439. f = rxvt_File_search_path( PKG_CONF_DIR, file, ext);
  440. }
  441. while(0);
  442. rxvt_dbgmsg ((DBG_DEBUG, DBG_MISC, "Got file %s\n", f ? f : "(nil)"));
  443. return f;
  444. }
  445. #endif /* defined (BACKGROUND_IMAGE) || (HAVE_MENUBAR) */
  446. /*----------------------------------------------------------------------*
  447. * miscellaneous drawing routines
  448. */
  449. # define CHOOSE_GC_FG(DISP, MYGC, PIXCOL) \
  450. XSetForeground ((DISP), (MYGC), (PIXCOL))
  451. /*
  452. * Draw top/left and bottom/right border shadows around windows
  453. */
  454. #if defined(RXVT_SCROLLBAR) || defined(HAVE_MENUBAR)
  455. /* EXTPROTO */
  456. void
  457. rxvt_draw_shadow (Display *Xdisplay, Window win, GC gc, unsigned long topShadow, unsigned long botShadow, int x, int y, int w, int h)
  458. {
  459. int shadow;
  460. shadow = (w == 0 || h == 0) ? 1 : SHADOW;
  461. w += x - 1;
  462. h += y - 1;
  463. for (; shadow-- > 0; x++, y++, w--, h--)
  464. {
  465. CHOOSE_GC_FG(Xdisplay, gc, topShadow);
  466. XDrawLine(Xdisplay, win, gc, x, y, w, y);
  467. XDrawLine(Xdisplay, win, gc, x, y, x, h);
  468. CHOOSE_GC_FG(Xdisplay, gc, botShadow);
  469. XDrawLine(Xdisplay, win, gc, w, h, w, y + 1);
  470. XDrawLine(Xdisplay, win, gc, w, h, x + 1, h);
  471. }
  472. }
  473. #endif
  474. /* button shapes */
  475. #ifdef HAVE_MENUBAR
  476. /* EXTPROTO */
  477. void
  478. rxvt_draw_triangle( Display *Xdisplay, Window win, GC gc,
  479. unsigned long topShadow, unsigned long botShadow,
  480. int x, int y, int w, int type)
  481. {
  482. switch (type)
  483. {
  484. case 'r': /* right triangle */
  485. CHOOSE_GC_FG(Xdisplay, gc, topShadow);
  486. XDrawLine(Xdisplay, win, gc, x, y, x, y + w);
  487. XDrawLine(Xdisplay, win, gc, x, y, x + w, y + w / 2);
  488. CHOOSE_GC_FG(Xdisplay, gc, botShadow);
  489. XDrawLine(Xdisplay, win, gc, x, y + w, x + w, y + w / 2);
  490. break;
  491. case 'l': /* left triangle */
  492. CHOOSE_GC_FG(Xdisplay, gc, botShadow);
  493. XDrawLine(Xdisplay, win, gc, x + w, y + w, x + w, y);
  494. XDrawLine(Xdisplay, win, gc, x + w, y + w, x, y + w / 2);
  495. CHOOSE_GC_FG(Xdisplay, gc, topShadow);
  496. XDrawLine(Xdisplay, win, gc, x, y + w / 2, x + w, y);
  497. break;
  498. case 'd': /* down triangle */
  499. CHOOSE_GC_FG(Xdisplay, gc, topShadow);
  500. XDrawLine(Xdisplay, win, gc, x, y, x + w / 2, y + w);
  501. XDrawLine(Xdisplay, win, gc, x, y, x + w, y);
  502. CHOOSE_GC_FG(Xdisplay, gc, botShadow);
  503. XDrawLine(Xdisplay, win, gc, x + w, y, x + w / 2, y + w);
  504. break;
  505. case 'u': /* up triangle */
  506. CHOOSE_GC_FG(Xdisplay, gc, botShadow);
  507. XDrawLine(Xdisplay, win, gc, x + w, y + w, x + w / 2, y);
  508. XDrawLine(Xdisplay, win, gc, x + w, y + w, x, y + w);
  509. CHOOSE_GC_FG(Xdisplay, gc, topShadow);
  510. XDrawLine(Xdisplay, win, gc, x, y + w, x + w / 2, y);
  511. break;
  512. }
  513. }
  514. #endif
  515. /*----------------------- end-of-file (C source) -----------------------*/