e93dba7dff5ecb5455815dab373fe028ed348f68
[dana/urxvt.git] / src / misc.C
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> Queen's Univ at Kingston
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) 2003-2004 Marc Lehmann <pcg@goof.com>
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
26 #include "../config.h"          /* NECESSARY */
27 #include "rxvt.h"               /* NECESSARY */
28 #include "misc.intpro"          /* PROTOS for internal routines */
29
30 char *
31 rxvt_wcstombs (const wchar_t *str, int len)
32 {
33   if (len < 0) len = wcslen (str);
34
35   mbstate mbs;
36   char *r = (char *)rxvt_malloc (len * MB_CUR_MAX + 1);
37
38   char *dst = r;
39   while (len--)
40     {
41       int l = wcrtomb (dst, *str++, mbs);
42       if (l < 0)
43         *dst++ = '?';
44       else
45         dst += l;
46     }
47
48   *dst++ = 0;
49
50   return r;
51 }
52
53 wchar_t *
54 rxvt_mbstowcs (const char *str, int len)
55 {
56   if (len < 0) len = strlen (str);
57
58   wchar_t *r = (wchar_t *)rxvt_malloc ((len + 1) * sizeof (wchar_t));
59
60   if (mbstowcs (r, str, len + 1) < 0)
61     *r = 0;
62
63   return r;
64 }
65
66 char *
67 rxvt_wcstoutf8 (const wchar_t *str, int len)
68 {
69   if (len < 0) len = wcslen (str);
70
71   char *r = (char *)rxvt_malloc (len * 4 + 1);
72   char *p = r;
73
74   while (len--)
75     {
76       unicode_t w = *str++;
77
78       if      (w < 0x000080)
79         *p++ = w;
80       else if (w < 0x000800)
81         *p++ = 0xc0 | ( w >>  6),
82         *p++ = 0x80 | ( w        & 0x3f);
83       else if (w < 0x010000)
84         *p++ = 0xe0 | ( w >> 12       ),
85         *p++ = 0x80 | ((w >>  6) & 0x3f),
86         *p++ = 0x80 | ( w        & 0x3f);
87       else if (w < 0x110000)
88         *p++ = 0xf0 | ( w >> 18),
89         *p++ = 0x80 | ((w >> 12) & 0x3f),
90         *p++ = 0x80 | ((w >>  6) & 0x3f),
91         *p++ = 0x80 | ( w        & 0x3f);
92       else
93         *p++ = '?';
94     }
95
96   *p = 0;
97
98   return r;
99 }
100
101 wchar_t *
102 rxvt_utf8towcs (const char *str, int len)
103 {
104   if (len < 0) len = strlen (str);
105
106   wchar_t *r = (wchar_t *)rxvt_malloc ((len + 1) * sizeof (wchar_t));
107   wchar_t *p = r;
108
109   unsigned char *s = (unsigned char *)str;
110
111   while (len)
112     {
113       if (s[0] < 0x80)
114         {
115           *p++ = *s++; len--;
116         }
117       else if (len > 0 
118                && s[0] >= 0xc2 && s[0] <= 0xdf 
119                && (s[1] & 0xc0) == 0x80)
120         {
121           *p++ = ((s[0] & 0x1f) << 6)
122                |  (s[1] & 0x3f);
123           s += 2; len -= 2;
124         }
125       else if (len > 1 
126                && (   (s[0] == 0xe0                 && s[1] >= 0xa0 && s[1] <= 0xbf)
127                    || (s[0] >= 0xe1 && s[0] <= 0xec && s[1] >= 0x80 && s[1] <= 0xbf)
128                    || (s[0] == 0xed                 && s[1] >= 0x80 && s[1] <= 0x9f)
129                    || (s[0] >= 0xee && s[0] <= 0xef && s[1] >= 0x80 && s[1] <= 0xbf)
130                   )
131                && (s[2] & 0xc0) == 0x80)
132         {
133           *p++ = ((s[0] & 0x0f) << 12)
134                | ((s[1] & 0x3f) <<  6)
135                |  (s[2] & 0x3f);
136           s += 3; len -= 3;
137         }
138       else if (len > 2
139                && (   (s[0] == 0xf0                 && s[1] >= 0x90 && s[1] <= 0xbf)
140                    || (s[0] >= 0xf1 && s[0] <= 0xf3 && s[1] >= 0x80 && s[1] <= 0xbf)
141                    || (s[0] == 0xf4                 && s[1] >= 0x80 && s[1] <= 0x8f)
142                   )
143                && (s[2] & 0xc0) == 0x80
144                && (s[3] & 0xc0) == 0x80)
145         {
146           *p++ = ((s[0] & 0x07) << 18) 
147                | ((s[1] & 0x3f) << 12) 
148                | ((s[2] & 0x3f) <<  6) 
149                |  (s[3] & 0x3f);
150           s += 4; len -= 4;
151         }
152       else
153         {
154           *p++ = 0xfffd;
155           s++; len--;
156         }
157     }
158
159   *p = 0;
160
161   return r;
162 }
163
164 char *
165 rxvt_strdup (const char *str)
166 {
167   return str ? strdup (str) : 0;
168 }
169
170 /* INTPROTO */
171 char *
172 rxvt_r_basename (const char *str)
173 {
174   char *base = strrchr (str, '/');
175
176   return (char *) (base ? base + 1 : str);
177 }
178
179 /*
180  * Print an error message
181  */
182 /* INTPROTO */
183 void
184 rxvt_vlog (const char *fmt, va_list arg_ptr)
185 {
186   char msg[1024];
187
188   vsnprintf (msg, sizeof msg, fmt, arg_ptr);
189
190   if (GET_R && GET_R->log_hook)
191     (*GET_R->log_hook) (msg);
192   else
193     write (STDOUT_FILENO, msg, strlen (msg));
194 }
195
196 /* INTPROTO */
197 void
198 rxvt_log (const char *fmt,...)
199 {
200   va_list arg_ptr;
201
202   va_start (arg_ptr, fmt);
203   rxvt_vlog (fmt, arg_ptr);
204   va_end (arg_ptr);
205 }
206
207 /*
208  * Print an error message
209  */
210 /* INTPROTO */
211 void
212 rxvt_warn (const char *fmt,...)
213 {
214   va_list arg_ptr;
215
216   rxvt_log ("%s: ", RESNAME);
217
218   va_start (arg_ptr, fmt);
219   rxvt_vlog (fmt, arg_ptr);
220   va_end (arg_ptr);
221 }
222
223 /* INTPROTO */
224 void
225 rxvt_fatal (const char *fmt,...)
226 {
227   va_list arg_ptr;
228
229   rxvt_log ("%s: ", RESNAME);
230
231   va_start (arg_ptr, fmt);
232   rxvt_vlog (fmt, arg_ptr);
233   va_end (arg_ptr);
234
235   rxvt_exit_failure ();
236 }
237
238 class rxvt_failure_exception rxvt_failure_exception;
239
240 /* INTPROTO */
241 void
242 rxvt_exit_failure ()
243 {
244   throw (rxvt_failure_exception);
245 }
246
247 /*
248  * check that the first characters of S1 match S2
249  *
250  * No Match
251  *      return: 0
252  * Match
253  *      return: strlen (S2)
254  */
255 /* INTPROTO */
256 int
257 rxvt_Str_match (const char *s1, const char *s2)
258 {
259   int n = strlen (s2);
260
261   return ((strncmp (s1, s2, n) == 0) ? n : 0);
262 }
263
264 /* INTPROTO */
265 const char *
266 rxvt_Str_skip_space (const char *str)
267 {
268   if (str)
269     while (*str && isspace (*str))
270       str++;
271
272   return str;
273 }
274
275 /*
276  * remove leading/trailing space and strip-off leading/trailing quotes.
277  * in place.
278  */
279 /* INTPROTO */
280 char           *
281 rxvt_Str_trim (char *str)
282 {
283   char *r, *s;
284   int n;
285
286   if (!str || !*str)            /* shortcut */
287     return str;
288
289   /* skip leading spaces */
290   for (s = str; *s && isspace (*s); s++) ;
291   /* goto end of string */
292   for (n = 0, r = s; *r++; n++) ;
293   r -= 2;
294   /* dump return */
295   if (n > 0 && *r == '\n')
296     n--, r--;
297   /* backtrack along trailing spaces */
298   for (; n > 0 && isspace (*r); r--, n--) ;
299   /* skip matching leading/trailing quotes */
300   if (*s == '"' && *r == '"' && n > 1)
301     {
302       s++;
303       n -= 2;
304     }
305
306   /* copy back over: forwards copy */
307   for (r = str; n; n--)
308     *r++ = *s++;
309   *r = '\0';
310
311   return str;
312 }
313
314 /*
315  * in-place interpretation of string:
316  *
317  *      backslash-escaped:      "\a\b\E\e\n\r\t", "\octal"
318  *      Ctrl chars:     ^@ .. ^_, ^?
319  *
320  *      Emacs-style:    "M-" prefix
321  *
322  * Also,
323  *      "M-x" prefixed strings, append "\r" if needed
324  *      "\E]" prefixed strings (XTerm escape sequence) append ST if needed
325  *
326  * returns the converted string length
327  */
328 /* INTPROTO */
329 int
330 rxvt_Str_escaped (char *str)
331 {
332   char            ch, *s, *d;
333   int             i, num, append = 0;
334
335   if (!str || !*str)
336     return 0;
337
338   d = s = str;
339
340   if (*s == 'M' && s[1] == '-')
341     {
342       /* Emacs convenience, replace leading `M-..' with `\E..' */
343       *d++ = C0_ESC;
344       s += 2;
345       if (toupper (*s) == 'X')
346         /* append carriage-return for `M-xcommand' */
347         for (*d++ = 'x', append = '\r', s++; isspace (*s); s++) ;
348     }
349   for (; (ch = *s++);)
350     {
351       if (ch == '\\')
352         {
353           ch = *s++;
354           if (ch >= '0' && ch <= '7')
355             {   /* octal */
356               num = ch - '0';
357               for (i = 0; i < 2; i++, s++)
358                 {
359                   ch = *s;
360                   if (ch < '0' || ch > '7')
361                     break;
362                   num = num * 8 + ch - '0';
363                 }
364               ch = (char)num;
365             }
366           else if (ch == 'a')
367             ch = C0_BEL;        /* bell */
368           else if (ch == 'b')
369             ch = C0_BS; /* backspace */
370           else if (ch == 'E' || ch == 'e')
371             ch = C0_ESC;        /* escape */
372           else if (ch == 'n')
373             ch = '\n';  /* newline */
374           else if (ch == 'r')
375             ch = '\r';  /* carriage-return */
376           else if (ch == 't')
377             ch = C0_HT; /* tab */
378         }
379       else if (ch == '^')
380         {
381           ch = *s++;
382           ch = toupper (ch);
383           ch = (ch == '?' ? 127 : (ch - '@'));
384         }
385       *d++ = ch;
386     }
387
388   /* ESC] is an XTerm escape sequence, must be terminated */
389   if (*str == '\0' && str[1] == C0_ESC && str[2] == ']')
390     append = CHAR_ST;
391
392   /* add trailing character as required */
393   if (append && d[-1] != append)
394     *d++ = append;
395   *d = '\0';
396
397   return (d - str);
398 }
399
400 /*
401  * Split a comma-separated string into an array, stripping leading and
402  * trailing spaces (and paired quotes) from each entry.  Empty strings
403  * are properly returned
404  * Caller should free each entry and array when done
405  */
406 /* INTPROTO */
407 char          **
408 rxvt_splitcommastring (const char *cs)
409 {
410   int             l, n, p;
411   const char     *s, *t;
412   char          **ret;
413
414   if ((s = cs) == NULL)
415     s = "";
416
417   for (n = 1, t = s; *t; t++)
418     if (*t == ',')
419       n++;
420   ret = (char **)malloc ((n + 1) * sizeof (char *));
421   ret[n] = NULL;
422
423   for (l = 0, t = s; l < n; l++)
424     {
425       for ( ; *t && *t != ','; t++) ;
426       p = t - s;
427       ret[l] = (char *)malloc (p + 1);
428       strncpy (ret[l], s, p);
429       ret[l][p] = '\0';
430       rxvt_Str_trim (ret[l]);
431       s = ++t;
432     }
433   return ret;
434 }
435
436 void
437 rxvt_freecommastring (char **cs)
438 {
439   for (int i = 0; cs[i]; ++i)
440     free (cs[i]);
441
442   free (cs);
443 }
444
445 /*----------------------------------------------------------------------*
446  * file searching
447  */
448
449 /* #define DEBUG_SEARCH_PATH */
450
451 #if defined (XPM_BACKGROUND) || (MENUBAR_MAX)
452 /*
453  * search for FILE in the current working directory, and within the
454  * colon-delimited PATHLIST, adding the file extension EXT if required.
455  *
456  * FILE is either semi-colon or zero terminated
457  */
458 /* INTPROTO */
459 char           *
460 rxvt_File_search_path (const char *pathlist, const char *file, const char *ext)
461 {
462   int             maxpath, len;
463   const char     *p, *path;
464   char            name[256];
465
466   if (!access (file, R_OK))     /* found (plain name) in current directory */
467     return strdup (file);
468
469   /* semi-colon delimited */
470   if ((p = strchr (file, ';')))
471     len = (p - file);
472   else
473     len = strlen (file);
474
475 #ifdef DEBUG_SEARCH_PATH
476   getcwd (name, sizeof (name));
477   fprintf (stderr, "pwd: \"%s\"\n", name);
478   fprintf (stderr, "find: \"%.*s\"\n", len, file);
479 #endif
480
481   /* leave room for an extra '/' and trailing '\0' */
482   maxpath = sizeof (name) - (len + (ext ? strlen (ext) : 0) + 2);
483   if (maxpath <= 0)
484     return NULL;
485
486   /* check if we can find it now */
487   strncpy (name, file, len);
488   name[len] = '\0';
489
490   if (!access (name, R_OK))
491     return strdup (name);
492   if (ext)
493     {
494       strcat (name, ext);
495       if (!access (name, R_OK))
496         return strdup (name);
497     }
498   for (path = pathlist; path != NULL && *path != '\0'; path = p)
499     {
500       int             n;
501
502       /* colon delimited */
503       if ((p = strchr (path, ':')) == NULL)
504         p = strchr (path, '\0');
505
506       n = (p - path);
507       if (*p != '\0')
508         p++;
509
510       if (n > 0 && n <= maxpath)
511         {
512           strncpy (name, path, n);
513           if (name[n - 1] != '/')
514             name[n++] = '/';
515           name[n] = '\0';
516           strncat (name, file, len);
517
518           if (!access (name, R_OK))
519             return strdup (name);
520           if (ext)
521             {
522               strcat (name, ext);
523               if (!access (name, R_OK))
524                 return strdup (name);
525             }
526         }
527     }
528   return NULL;
529 }
530
531 /* INTPROTO */
532 char           *
533 rxvt_File_find (const char *file, const char *ext, const char *path)
534 {
535   char           *f;
536
537   if (file == NULL || *file == '\0')
538     return NULL;
539
540   /* search environment variables here too */
541   if ((f = rxvt_File_search_path (path, file, ext)) == NULL)
542 #ifdef PATH_ENV
543     if ((f = rxvt_File_search_path (getenv (PATH_ENV), file, ext)) == NULL)
544 #endif
545       f = rxvt_File_search_path (getenv ("PATH"), file, ext);
546
547 #ifdef DEBUG_SEARCH_PATH
548   if (f)
549     fprintf (stderr, "found: \"%s\"\n", f);
550 #endif
551
552   return f;
553 }
554 #endif                          /* defined (XPM_BACKGROUND) || (MENUBAR_MAX) */
555
556 /*----------------------------------------------------------------------*
557  * miscellaneous drawing routines
558  */
559
560 /*
561  * Draw top/left and bottom/right border shadows around windows
562  */
563 #if defined(RXVT_SCROLLBAR) || defined(MENUBAR)
564 /* INTPROTO */
565 void
566 rxvt_Draw_Shadow (Display *display, Window win, GC topShadow, GC botShadow, int x, int y, int w, int h)
567 {
568   int             shadow;
569
570   shadow = (w == 0 || h == 0) ? 1 : SHADOW;
571   w += x - 1;
572   h += y - 1;
573   for (; shadow-- > 0; x++, y++, w--, h--)
574     {
575       XDrawLine (display, win, topShadow, x, y, w, y);
576       XDrawLine (display, win, topShadow, x, y, x, h);
577       XDrawLine (display, win, botShadow, w, h, w, y + 1);
578       XDrawLine (display, win, botShadow, w, h, x + 1, h);
579     }
580 }
581 #endif
582
583 /* button shapes */
584 #ifdef MENUBAR
585 /* INTPROTO */
586 void
587 rxvt_Draw_Triangle (Display *display, Window win, GC topShadow, GC botShadow, int x, int y, int w, int type)
588 {
589   switch (type)
590     {
591       case 'r':                 /* right triangle */
592         XDrawLine (display, win, topShadow, x, y, x, y + w);
593         XDrawLine (display, win, topShadow, x, y, x + w, y + w / 2);
594         XDrawLine (display, win, botShadow, x, y + w, x + w, y + w / 2);
595         break;
596
597       case 'l':                 /* left triangle */
598         XDrawLine (display, win, botShadow, x + w, y + w, x + w, y);
599         XDrawLine (display, win, botShadow, x + w, y + w, x, y + w / 2);
600         XDrawLine (display, win, topShadow, x, y + w / 2, x + w, y);
601         break;
602
603       case 'd':                 /* down triangle */
604         XDrawLine (display, win, topShadow, x, y, x + w / 2, y + w);
605         XDrawLine (display, win, topShadow, x, y, x + w, y);
606         XDrawLine (display, win, botShadow, x + w, y, x + w / 2, y + w);
607         break;
608
609       case 'u':                 /* up triangle */
610         XDrawLine (display, win, botShadow, x + w, y + w, x + w / 2, y);
611         XDrawLine (display, win, botShadow, x + w, y + w, x, y + w);
612         XDrawLine (display, win, topShadow, x, y + w, x + w / 2, y);
613         break;
614 #if 0
615       case 's':                 /* square */
616         XDrawLine (display, win, topShadow, x + w, y, x, y);
617         XDrawLine (display, win, topShadow, x, y, x, y + w);
618         XDrawLine (display, win, botShadow, x, y + w, x + w, y + w);
619         XDrawLine (display, win, botShadow, x + w, y + w, x + w, y);
620         break;
621 #endif
622
623     }
624 }
625 #endif
626 /*----------------------- end-of-file (C source) -----------------------*/