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