Back to index

lightning-sunbird  0.9+nobinonly
pango-utils.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  * Pango
00003  * pango-utils.c: Utilities for internal functions and modules
00004  *
00005  * ***** BEGIN LICENSE BLOCK *****
00006  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00007  *
00008  * The contents of this file are subject to the Mozilla Public License Version
00009  * 1.1 (the "License"); you may not use this file except in compliance with
00010  * the License. You may obtain a copy of the License at
00011  * http://www.mozilla.org/MPL/
00012  *
00013  * Software distributed under the License is distributed on an "AS IS" basis,
00014  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00015  * for the specific language governing rights and limitations under the
00016  * License.
00017  *
00018  * The Original Code is the Pango Library (www.pango.org).
00019  *
00020  * The Initial Developer of the Original Code is
00021  * Red Hat Software.
00022  * Portions created by the Initial Developer are Copyright (C) 2000
00023  * the Initial Developer. All Rights Reserved.
00024  *
00025  * Contributor(s):
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include <ctype.h>
00042 #include <errno.h>
00043 #include <string.h>
00044 #include <stdlib.h>
00045 
00046 #include "pango-utils.h"
00047 
00048 #ifdef HAVE_FRIBIDI
00049 #include <fribidi/fribidi.h>
00050 #endif
00051 
00052 #ifndef HAVE_FLOCKFILE
00053 #define flockfile(f) (void)1
00054 #define funlockfile(f) (void)1
00055 #define getc_unlocked(f) getc(f)
00056 #endif /* !HAVE_FLOCKFILE */
00057 
00058 #ifdef G_OS_WIN32
00059 #include <sys/types.h>
00060 #define STRICT
00061 #include <windows.h>
00062 #endif
00063 
00064 #define UTF8_COMPUTE(Char, Mask, Len)     \
00065   if (Char < 128)    { \
00066     Len = 1; \
00067     Mask = 0x7f;     \
00068   }    \
00069   else if ((Char & 0xe0) == 0xc0)  { \
00070     Len = 2;  \
00071     Mask = 0x1f;     \
00072   }    \
00073   else if ((Char & 0xf0) == 0xe0)  { \
00074     Len = 3; \
00075     Mask = 0x0f; \
00076   }    \
00077   else if ((Char & 0xf8) == 0xf0)  { \
00078     Len = 4; \
00079     Mask = 0x07; \
00080   }    \
00081   else if ((Char & 0xfc) == 0xf8)  { \
00082     Len = 5; \
00083     Mask = 0x03; \
00084   }    \
00085   else if ((Char & 0xfe) == 0xfc)  { \
00086     Len = 6; \
00087     Mask = 0x01; \
00088   }    \
00089   else \
00090    Len = -1;
00091 
00092 #define UTF8_LENGTH(Char) \
00093   ((Char) < 0x80 ? 1 : \
00094    ((Char) < 0x800 ? 2 :  \
00095     ((Char) < 0x10000 ? 3 : \
00096      ((Char) < 0x200000 ? 4 : \
00097       ((Char) < 0x4000000 ? 5 : 6)))))
00098 
00099 #define UTF8_GET(Result, Chars, Count, Mask, Len)       \
00100   (Result) = (Chars)[0] & (Mask);  \
00101   for ((Count) = 1; (Count) < (Len); ++(Count))  { \
00102     if (((Chars)[(Count)] & 0xc0) != 0x80) { \
00103             (Result) = -1; \
00104             break; \
00105          }    \
00106     (Result) <<= 6;  \
00107     (Result) |= ((Chars)[(Count)] & 0x3f); \
00108   }
00109 
00110 #define UNICODE_VALID(Char) \
00111   ((Char) < 0x110000 && ((Char) < 0xD800 || (Char) >= 0xE000) && \
00112    (Char) != 0xFFFE && (Char) != 0xFFFF)
00113 
00122 char *
00123 pangolite_trim_string (const char *str)
00124 {
00125   int len;
00126 
00127   g_return_val_if_fail (str != NULL, NULL);
00128   
00129   while (*str && isspace (*str))
00130     str++;
00131 
00132   len = strlen (str);
00133   while (len > 0 && isspace (str[len-1]))
00134     len--;
00135 
00136   return g_strndup (str, len);
00137 }
00138 
00148 char **
00149 pangolite_split_file_list (const char *str)
00150 {
00151   int i = 0;
00152   int j;
00153   char **files;
00154 
00155   files = g_strsplit (str, G_SEARCHPATH_SEPARATOR_S, -1);
00156 
00157   while (files[i])
00158     {
00159       char *file = pangolite_trim_string (files[i]);
00160 
00161       /* If the resulting file is empty, skip it */
00162       if (file[0] == '\0')
00163        {
00164          g_free(file);
00165          g_free (files[i]);
00166          
00167          for (j = i + 1; files[j]; j++)
00168            files[j - 1] = files[j];
00169          
00170          files[j - 1] = NULL;
00171 
00172          continue;
00173        }
00174 #ifndef G_OS_WIN32
00175       /* '~' is a quite normal and common character in file names on
00176        * Windows, especially in the 8.3 versions of long file names, which
00177        * still occur and then. Also, few Windows user are aware of the
00178        * Unix shell convention that '~' stands for the home directory,
00179        * even if they happen to have a home directory.
00180        */
00181       if (file[0] == '~' && file[1] == G_DIR_SEPARATOR)
00182        {
00183          char *tmp = g_strconcat (g_get_home_dir(), file + 1, NULL);
00184          g_free (file);
00185          file = tmp;
00186        }
00187 #endif
00188       g_free (files[i]);
00189       files[i] = file;
00190        
00191       i++;
00192     }
00193 
00194   return files;
00195 }
00196 
00214 gint
00215 pangolite_read_line (FILE *stream, GString *str)
00216 {
00217   gboolean quoted = FALSE;
00218   gboolean comment = FALSE;
00219   int n_read = 0;
00220   int lines = 1;
00221   
00222   flockfile (stream);
00223 
00224   g_string_truncate (str, 0);
00225   
00226   while (1)
00227     {
00228       int c;
00229       
00230       c = getc_unlocked (stream);
00231 
00232       if (c == EOF)
00233        {
00234          if (quoted)
00235            g_string_append_c (str, '\\');
00236          
00237          goto done;
00238        }
00239       else
00240        n_read++;
00241 
00242       if (quoted)
00243        {
00244          quoted = FALSE;
00245          
00246          switch (c)
00247            {
00248            case '#':
00249              g_string_append_c (str, '#');
00250              break;
00251            case '\r':
00252            case '\n':
00253              {
00254               int next_c = getc_unlocked (stream);
00255 
00256               if (!(next_c == EOF ||
00257                     (c == '\r' && next_c == '\n') ||
00258                     (c == '\n' && next_c == '\r')))
00259                 ungetc (next_c, stream);
00260 
00261               lines++;
00262               
00263               break;
00264              }
00265            default:
00266              g_string_append_c (str, '\\');            
00267              g_string_append_c (str, c);
00268            }
00269        }
00270       else
00271        {
00272          switch (c)
00273            {
00274            case '#':
00275              comment = TRUE;
00276              break;
00277            case '\\':
00278              if (!comment)
00279               quoted = TRUE;
00280              break;
00281            case '\n':
00282              {
00283               int next_c = getc_unlocked (stream);
00284 
00285               if (!(c == EOF ||
00286                     (c == '\r' && next_c == '\n') ||
00287                     (c == '\n' && next_c == '\r')))
00288                 ungetc (next_c, stream);
00289 
00290               goto done;
00291              }
00292            default:
00293              if (!comment)
00294               g_string_append_c (str, c);
00295            }
00296        }
00297     }
00298 
00299  done:
00300 
00301   funlockfile (stream);
00302 
00303   return (n_read > 0) ? lines : 0;
00304 }
00305 
00315 gboolean
00316 pangolite_skip_space (const char **pos)
00317 {
00318   const char *p = *pos;
00319   
00320   while (isspace (*p))
00321     p++;
00322 
00323   *pos = p;
00324 
00325   return !(*p == '\0');
00326 }
00327 
00339 gboolean
00340 pangolite_scan_word (const char **pos, GString *out)
00341 {
00342   const char *p = *pos;
00343 
00344   while (isspace (*p))
00345     p++;
00346   
00347   if (!((*p >= 'A' && *p <= 'Z') ||
00348        (*p >= 'a' && *p <= 'z') ||
00349        *p == '_'))
00350     return FALSE;
00351 
00352   g_string_truncate (out, 0);
00353   g_string_append_c (out, *p);
00354   p++;
00355 
00356   while ((*p >= 'A' && *p <= 'Z') ||
00357         (*p >= 'a' && *p <= 'z') ||
00358         (*p >= '0' && *p <= '9') ||
00359         *p == '_')
00360     {
00361       g_string_append_c (out, *p);
00362       p++;
00363     }
00364 
00365   *pos = p;
00366 
00367   return TRUE;
00368 }
00369 
00382 gboolean
00383 pangolite_scan_string (const char **pos, GString *out)
00384 {
00385   const char *p = *pos;
00386   
00387   while (isspace (*p))
00388     p++;
00389 
00390   if (!*p)
00391     return FALSE;
00392   else if (*p == '"')
00393     {
00394       gboolean quoted = FALSE;
00395       g_string_truncate (out, 0);
00396 
00397       p++;
00398 
00399       while (TRUE)
00400        {
00401          if (quoted)
00402            {
00403              int c = *p;
00404              
00405              switch (c)
00406               {
00407               case '\0':
00408                 return FALSE;
00409               case 'n':
00410                 c = '\n';
00411                 break;
00412               case 't':
00413                 c = '\t';
00414                 break;
00415               }
00416              
00417              quoted = FALSE;
00418              g_string_append_c (out, c);
00419            }
00420          else
00421            {
00422              switch (*p)
00423               {
00424               case '\0':
00425                 return FALSE;
00426               case '\\':
00427                 quoted = TRUE;
00428                 break;
00429               case '"':
00430                 p++;
00431                 goto done;
00432               default:
00433                 g_string_append_c (out, *p);
00434                 break;
00435               }
00436            }
00437          p++;
00438        }
00439     done:
00440       ;
00441     }
00442   else
00443     {
00444       g_string_truncate (out, 0);
00445 
00446       while (*p && !isspace (*p))
00447        {
00448          g_string_append_c (out, *p);
00449          p++;
00450        }
00451     }
00452 
00453   *pos = p;
00454 
00455   return TRUE;
00456 }
00457 
00458 gboolean
00459 pangolite_scan_int (const char **pos, int *out)
00460 {
00461   int i = 0;
00462   char buf[32];
00463   const char *p = *pos;
00464 
00465   while (isspace (*p))
00466     p++;
00467   
00468   if (*p < '0' || *p > '9')
00469     return FALSE;
00470 
00471   while ((*p >= '0') && (*p <= '9') && i < sizeof(buf))
00472     {
00473       buf[i] = *p;
00474       i++;
00475       p++;
00476     }
00477 
00478   if (i == sizeof(buf))
00479     return FALSE;
00480   else
00481     buf[i] = '\0';
00482 
00483   *out = atoi (buf);
00484 
00485   return TRUE;
00486 }
00487 
00488 static GHashTable *config_hash = NULL;
00489 
00490 static void
00491 read_config_file (const char *filename, gboolean enoent_error)
00492 {
00493   FILE *file;
00494     
00495   GString *line_buffer;
00496   GString *tmp_buffer1;
00497   GString *tmp_buffer2;
00498   char *errstring = NULL;
00499   const char *pos;
00500   char *section = NULL;
00501   int line = 0;
00502 
00503   file = fopen (filename, "r");
00504   if (!file)
00505     {
00506       if (errno != ENOENT || enoent_error)
00507        fprintf (stderr, "Pangolite:%s: Error opening config file: %s\n",
00508                filename, g_strerror (errno));
00509       return;
00510     }
00511   
00512   line_buffer = g_string_new (NULL);
00513   tmp_buffer1 = g_string_new (NULL);
00514   tmp_buffer2 = g_string_new (NULL);
00515 
00516   while (pangolite_read_line (file, line_buffer))
00517     {
00518       line++;
00519 
00520       pos = line_buffer->str;
00521       if (!pangolite_skip_space (&pos))
00522        continue;
00523 
00524       if (*pos == '[')      /* Section */
00525        {
00526          pos++;
00527          if (!pangolite_skip_space (&pos) ||
00528              !pangolite_scan_word (&pos, tmp_buffer1) ||
00529              !pangolite_skip_space (&pos) ||
00530              *(pos++) != ']' ||
00531              pangolite_skip_space (&pos))
00532            {
00533              errstring = g_strdup ("Error parsing [SECTION] declaration");
00534              goto error;
00535            }
00536 
00537          section = g_strdup (tmp_buffer1->str);
00538        }
00539       else                  /* Key */
00540        {
00541          gboolean empty = FALSE;
00542          gboolean append = FALSE;
00543          char *k, *v;
00544 
00545          if (!section)
00546            {
00547              errstring = g_strdup ("A [SECTION] declaration must occur first");
00548              goto error;
00549            }
00550 
00551          if (!pangolite_scan_word (&pos, tmp_buffer1) ||
00552              !pangolite_skip_space (&pos))
00553            {
00554              errstring = g_strdup ("Line is not of the form KEY=VALUE or KEY+=VALUE");
00555              goto error;
00556            }
00557          if (*pos == '+')
00558            {
00559              append = TRUE;
00560              pos++;
00561            }
00562 
00563          if (*(pos++) != '=')
00564            {
00565              errstring = g_strdup ("Line is not of the form KEY=VALUE or KEY+=VALUE");
00566              goto error;
00567            }
00568            
00569          if (!pangolite_skip_space (&pos))
00570            {
00571              empty = TRUE;
00572            }
00573          else
00574            {
00575              if (!pangolite_scan_string (&pos, tmp_buffer2))
00576               {
00577                 errstring = g_strdup ("Error parsing value string");
00578                 goto error;
00579               }
00580              if (pangolite_skip_space (&pos))
00581               {
00582                 errstring = g_strdup ("Junk after value string");
00583                 goto error;
00584               }
00585            }
00586 
00587          g_string_prepend_c (tmp_buffer1, '/');
00588          g_string_prepend (tmp_buffer1, section);
00589 
00590          /* Remove any existing values */
00591          if (g_hash_table_lookup_extended (config_hash, tmp_buffer1->str,
00592                                        (gpointer *)&k, (gpointer *)&v))
00593            {
00594              g_free (k);
00595              if (append)
00596               {
00597                 g_string_prepend (tmp_buffer2, v);
00598                 g_free (v);
00599               }
00600            }
00601              
00602          if (!empty)
00603            {
00604              g_hash_table_insert (config_hash,
00605                                g_strdup (tmp_buffer1->str),
00606                                g_strdup (tmp_buffer2->str));
00607            }
00608        }
00609     }
00610       
00611   if (ferror (file))
00612     errstring = g_strdup ("g_strerror(errno)");
00613   
00614  error:
00615 
00616   if (errstring)
00617     {
00618       fprintf (stderr, "Pangolite:%s:%d: %s\n", filename, line, errstring);
00619       g_free (errstring);
00620     }
00621       
00622   g_free (section);
00623   g_string_free (line_buffer, TRUE);
00624   g_string_free (tmp_buffer1, TRUE);
00625   g_string_free (tmp_buffer2, TRUE);
00626 
00627   fclose (file);
00628 }
00629 
00630 static void
00631 read_config ()
00632 {
00633   if (!config_hash)
00634     {
00635       char *filename;
00636       char *home;
00637       
00638       config_hash = g_hash_table_new (g_str_hash, g_str_equal);
00639       filename = g_strconcat (pangolite_get_sysconf_subdirectory (),
00640                            G_DIR_SEPARATOR_S "pangoliterc",
00641                            NULL);
00642       read_config_file (filename, FALSE);
00643       g_free (filename);
00644 
00645       home = g_get_home_dir ();
00646       if (home && *home)
00647        {
00648          filename = g_strconcat (home,
00649                               G_DIR_SEPARATOR_S ".pangoliterc",
00650                               NULL);
00651          read_config_file (filename, FALSE);
00652          g_free (filename);
00653        }
00654 
00655       filename = g_getenv ("PANGO_RC_FILE");
00656       if (filename)
00657        read_config_file (filename, TRUE);
00658     }
00659 }
00660 
00672 char *
00673 pangolite_config_key_get (const char *key)
00674 {
00675   g_return_val_if_fail (key != NULL, NULL);
00676   
00677   read_config ();
00678 
00679   return g_strdup (g_hash_table_lookup (config_hash, key));
00680 }
00681 
00682 G_CONST_RETURN char *
00683 pangolite_get_sysconf_subdirectory (void)
00684 {
00685 #ifdef G_OS_WIN32
00686   static gchar *result = NULL;
00687 
00688   if (result == NULL)
00689     result = g_win32_get_package_installation_subdirectory
00690       ("pangolite", g_strdup_printf ("pangolite-%s.dll", PANGO_VERSION), "etc\\pangolite");
00691 
00692   return result;
00693   // Am open to any other way of doing this
00694   // Bottomline : need to provide path to pango.modules
00695   /* Currently set to dist/bin - Am open to other location */
00696 #else
00697   char *tmp = getenv("MOZILLA_FIVE_HOME");
00698   return tmp;
00699 #endif
00700 }
00701 
00702 G_CONST_RETURN char *
00703 pangolite_get_lib_subdirectory (void)
00704 {
00705 #ifdef G_OS_WIN32
00706   static gchar *result = NULL;
00707 
00708   if (result == NULL)
00709     result = g_win32_get_package_installation_subdirectory
00710       ("pangolite", g_strdup_printf ("pangolite-%s.dll", PANGO_VERSION), "lib\\pangolite");
00711 
00712   return result;
00713   // Open to any other way of doing this
00714   // Bottomline : need to provide path to pangolite libraries
00715   // Currently set to dist/bin - Open to other locations
00716 #else
00717   char *tmp = getenv("MOZILLA_FIVE_HOME");
00718   return tmp;
00719   /*  return "/home/prabhath/PROJ/opt/lib"; */
00720 #endif
00721 }
00722 
00732 gunichar
00733 g_utf8_get_char (const gchar *p)
00734 {
00735   int i, mask = 0, len;
00736   gunichar result;
00737   unsigned char c = (unsigned char) *p;
00738 
00739   UTF8_COMPUTE (c, mask, len);
00740   if (len == -1)
00741     return (gunichar)-1;
00742   UTF8_GET (result, p, i, mask, len);
00743 
00744   return result;
00745 }
00746 
00747 
00748 #ifdef HAVE_FRIBIDI
00749 void 
00750 pangolite_log2vis_get_embedding_levels (gunichar       *str,
00751                                     int            len,
00752                                     PangoliteDirection *pbase_dir,
00753                                     guint8         *embedding_level_list)
00754 {
00755   FriBidiCharType fribidi_base_dir;
00756   
00757   fribidi_base_dir = (*pbase_dir == PANGO_DIRECTION_LTR) ?
00758     FRIBIDI_TYPE_L : FRIBIDI_TYPE_R;
00759   
00760   fribidi_log2vis_get_embedding_levels(str, len, &fribidi_base_dir,
00761                                        embedding_level_list);
00762   
00763   *pbase_dir = (fribidi_base_dir == FRIBIDI_TYPE_L) ?  
00764     PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL;
00765 }
00766 
00767 gboolean 
00768 pangolite_get_mirror_char (gunichar ch, gunichar *mirrored_ch)
00769 {
00770   return fribidi_get_mirror_char (ch, mirrored_ch); 
00771 }
00772 #endif /* HAVE_FRIBIDI */