home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / programming / andylib_1 / AndyLib / c / options < prev    next >
Text File  |  1995-05-15  |  13KB  |  547 lines

  1. /* options.c */
  2.  
  3. #include <ctype.h>
  4. #include <string.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <stdarg.h>
  8.  
  9. #include "os.h"
  10. #include "swis.h"
  11. #include "wimp.h"
  12. #include "wimpt.h"
  13. #include "win.h"
  14. #include "msgs.h"
  15. #include "flex.h"
  16.  
  17. #include "intalk.h"
  18. #include "options.h"
  19.  
  20. #define options_NAME   "<AWOptions$Dir>.Options"
  21. #define options_AWPATH "AWShared$Path"
  22. #define options_VAR    "AWOptions$Dir"
  23. #define options_CMD    "<AWOptions$Dir>"
  24.    
  25. typedef struct options__watcher
  26. {  struct options__watcher *link;
  27.    options_changedhandler ch;
  28.    void *handle;
  29. } options__watcher;
  30.  
  31. static char *options;    /* flex */
  32. static int  options__size;
  33. static options__watcher *options__world;
  34. static BOOL options__changed;
  35. static BOOL options__internal;
  36.  
  37. #define options__taginitchar(c) ((c) == '_' || (c) == '$' || isalpha(c))
  38. #define options__tagchar(c) (options__taginitchar(c) || isdigit(c))
  39.  
  40. static os_error *options__err(char *msg, ...)
  41. {  static os_error eb;
  42.    va_list ap;
  43.  
  44.    va_start(ap, msg);
  45.    vsprintf(eb.errmess, msgs_lookup(msg), ap);
  46.    va_end(ap);
  47.  
  48.    return &eb;
  49. }
  50.  
  51. static int options__strcicmp(const char *s1, const char *s2)
  52. {  while (*s1 && toupper(*s1) == toupper(*s2))
  53.       s1++, s2++;
  54.  
  55.    return toupper(*s1) - toupper(*s2);
  56. }
  57.  
  58. static os_error *options__falloc(void **ptr, int size)
  59. {  if (!flex_alloc(ptr, size))
  60.       return options__err("options9:Not enough memory to load or process options file");
  61.  
  62.    return NULL;
  63. }
  64.  
  65. static os_error *options__fextend(void **ptr, int by)
  66. {  if (!flex_extend(ptr, by))
  67.       return options__err("options9:Not enough memory to load or process options file");
  68.  
  69.    return NULL;
  70. }
  71.  
  72. static os_error *options__fmidextend(void **ptr, int at, int by)
  73. {  if (!flex_midextend(ptr, at, by))
  74.       return options__err("options9:Not enough memory to load or process options file");
  75.  
  76.    return NULL;
  77. }
  78.  
  79. #if 0
  80. static void options__ffree(void **ptr)
  81. {  if (*ptr) flex_free(ptr);
  82. }
  83. #endif
  84.  
  85. static os_error *options__malloc(void **ptr, int size)
  86. {  void *m;
  87.  
  88.    if (m = malloc(size), !m)
  89.       return options__err("options9:Not enough memory to load or process options file");
  90.  
  91.    *ptr = m;
  92.    return NULL;
  93. }
  94.  
  95. static void options__free(void **ptr)
  96. {  free(*ptr);
  97.    *ptr = NULL;
  98. }
  99.  
  100. static os_error *options__syntax(void)
  101. {  int lp, ln;
  102.  
  103.    for (lp = 0, ln = 1; lp < options__size; )
  104.    {  while (lp < options__size && options[lp] != '\n' && isspace(options[lp]))
  105.          lp++;
  106.          
  107.       if (lp >= options__size) goto endfile;
  108.  
  109.       switch (options[lp])
  110.       {  case '\n':
  111.             lp++;
  112.             ln++;
  113.             break;
  114.             
  115.          case '#': case ';': case '|':
  116.             lp++;
  117.             while (lp < options__size && options[lp] != '\n')
  118.                lp++;
  119.                
  120.             if (lp >= options__size)
  121.                goto missingeol;
  122.             lp++;
  123.             ln++;
  124.             break;
  125.  
  126.          default:
  127.             if (!options__taginitchar(options[lp]))
  128.                goto badchar;
  129.                
  130.             lp++;
  131.  
  132.             while (lp < options__size && options__tagchar(options[lp]))
  133.                lp++;
  134.  
  135.             if (lp >= options__size)
  136.                goto missingeol;
  137.  
  138.             if (options[lp] != ':')
  139.                return options__err("options5:Missing ':' after tag in line %d of options file", ln);
  140.  
  141.             lp++;
  142.  
  143.             while (lp < options__size && options[lp] != '\n')
  144.                lp++;
  145.                
  146.             if (lp >= options__size)
  147.                goto missingeol;
  148.             lp++;
  149.             ln++;
  150.             break;
  151.       }
  152.    }
  153.  
  154. endfile:
  155.    return NULL;
  156.  
  157. badchar:
  158.    return options__err("options4:Bad character in options tag in line %d", ln);
  159.  
  160. missingeol:
  161.    return options__err("options3:Missing newline before end of options file");
  162. }
  163.  
  164. static os_error *options__notify(void)
  165. {  os_error *err;
  166.    options__watcher *watcher;
  167.  
  168.    for (watcher = options__world; watcher; watcher = watcher->link)
  169.       if (err = watcher->ch(watcher->handle), err)
  170.          return err;
  171.  
  172.    return NULL;
  173. }   
  174.  
  175. /* Load the options file */
  176.  
  177. os_error *options_reload(void)
  178. {  os_regset regs;
  179.    os_filestr fcb;
  180.    os_error *err;
  181.  
  182.    regs.r[0] = (int) options_AWPATH;
  183.    regs.r[1] = NULL;
  184.    regs.r[2] = -1;
  185.    regs.r[3] = 0;
  186.    regs.r[4] = 0;
  187.    os_swix(OS_ReadVarVal, ®s);
  188.  
  189.    if (regs.r[2] == 0)
  190.       return options__err("options1:AWShared directory not found");
  191.  
  192.    fcb.action = 17;
  193.    fcb.name   = options_NAME;
  194.  
  195.    if (err = os_file(&fcb), err)
  196.       return err;
  197.  
  198.    if (fcb.action == 0)  /* not found isn't an error */
  199.    {  options__size = 0;
  200.       return NULL;
  201.    }
  202.    else if (fcb.action != 1)
  203.    {  fcb.loadaddr = fcb.action;
  204.       fcb.action   = 19;
  205.       return os_file(&fcb);
  206.    }
  207.  
  208.    if ((fcb.loadaddr & 0xFFFFFF00) != 0xFFFFFF00)
  209.       return options__err("options2:" options_NAME " is not a text file");
  210.  
  211.    options__size = fcb.start;
  212.    if (err = options__falloc((void **) &options, options__size), err)
  213.       return err;
  214.  
  215.    fcb.action   = 0xff;
  216.    fcb.loadaddr = (int) options;
  217.    fcb.execaddr = 0;
  218.  
  219.    if (err = os_file(&fcb), err)
  220.       return err;
  221.  
  222.    if (err = options__syntax(), err)
  223.       return err;
  224.  
  225.    options__changed = FALSE;
  226.  
  227.    return options__notify();
  228. }
  229.  
  230. static BOOL options__ukhandler(wimp_eventstr *e, void *handle)
  231. {  handle = handle;
  232.  
  233.    switch (e->e)
  234.    {  case wimp_ESEND:
  235.       case wimp_ESENDWANTACK:
  236.          switch (e->data.msg.hdr.action)
  237.          {  case intalk_MOPTIONSCHANGED:
  238.                if (options__internal)
  239.                   options__internal = FALSE;
  240.                else
  241.                   wimpt_complain(options_reload());
  242.          }
  243.    }
  244.    
  245.    return FALSE;
  246. }
  247.  
  248. os_error *options_init(void)
  249. {  options__world = NULL;
  250.    win_add_unknown_event_processor(options__ukhandler, &options__world);
  251.    return options_reload();
  252. }
  253.  
  254. /* Lines must be one of
  255.  *
  256.  * blank
  257.  * # comment
  258.  * tag:value
  259.  */
  260.  
  261. static int options__findtag(char *tag, int *solp)
  262. {  int lp, sol;
  263.    char *tp;
  264.  
  265.    for (lp = 0; lp < options__size; )
  266.    {  sol = lp;  /* start of line */
  267.    
  268.       while (lp < options__size && options[lp] != '\n' && isspace(options[lp]))
  269.          lp++;
  270.          
  271.       if (lp >= options__size) return -1;
  272.  
  273.       switch (options[lp])
  274.       {  case '\n':
  275.             lp++;
  276.             break;
  277.  
  278.          default:
  279.             if (options__taginitchar(options[lp]))
  280.             {  for (tp = tag;
  281.                     lp < options__size &&
  282.                     options__tagchar(options[lp]) && *tp &&
  283.                     tolower(options[lp]) == tolower(*tp); lp++, tp++)
  284.                   ;
  285.  
  286.                if (lp >= options__size)
  287.                   return -1;
  288.  
  289.                if (options[lp] == ':' && *tp == '\0')
  290.                {  if (solp) *solp = sol;
  291.                   return lp+1;
  292.                }
  293.             }
  294.  
  295.             /* fall through */
  296.  
  297.          case '#': case ';': case '|':
  298.             lp++;
  299.             while (lp < options__size && options[lp] != '\n')
  300.                lp++;
  301.                
  302.             if (lp >= options__size)
  303.                return -1;
  304.             lp++;
  305.             break;
  306.       }
  307.    }
  308.    
  309.    return -1;
  310. }
  311.  
  312. os_error *options_readstrx(char *tag, char *buf, int size, char *def)
  313. {  int pos;
  314.  
  315.    if (pos = options__findtag(tag, NULL), pos >= 0)
  316.    {  while (pos < options__size && options[pos] != '\n' && size > 1)
  317.       {  *buf++ = options[pos++];
  318.          size--;
  319.       }
  320.       *buf = '\0';
  321.    }
  322.    else if (def)
  323.       strcpy(buf, def);
  324.    else
  325.       *buf = '\0';
  326.  
  327.    return NULL;
  328. }
  329.  
  330. os_error *options_readstr(char *tag, char *buf, int size)
  331. {  return options_readstrx(tag, buf, size, NULL);
  332. }
  333.  
  334. os_error *options_readintx(char *tag, int *num, int def)
  335. {  char nbuf[20], dbuf[20], *np;
  336.    os_error *err;
  337.  
  338.    sprintf(dbuf, "%d", def);
  339.  
  340.    if (err = options_readstrx(tag, nbuf, 20, dbuf), err)
  341.       return err;
  342.  
  343.    for (np = nbuf; *np; np++)
  344.       if (!isdigit(*np))
  345.          return options__err("options6:%s is not a numeric option", tag);
  346.    
  347.    if (num) *num = atoi(nbuf);
  348.    
  349.    return NULL;
  350. }
  351.  
  352. os_error *options_readint(char *tag, int *num)
  353. {  return options_readintx(tag, num, 0);
  354. }
  355.  
  356. os_error *options_readboolx(char *tag, BOOL *flag, BOOL def)
  357. {  char bbuf[20], dbuf[5];
  358.    os_error *err;
  359.  
  360.    strcpy(dbuf, def ? "yes" : "no");
  361.  
  362.    if (err = options_readstrx(tag, bbuf, 20, dbuf), err)
  363.       return err;
  364.  
  365.    if (options__strcicmp(bbuf, "yes") == 0)
  366.       *flag = TRUE;
  367.    else if (*bbuf == '\0' || options__strcicmp(bbuf, "no") == 0)
  368.       *flag = FALSE;
  369.    else
  370.       return options__err("options7:%s is not a boolean option (yes or no required)", tag);
  371.       
  372.    return NULL;
  373. }
  374.  
  375. os_error *options_readbool(char *tag, BOOL *flag)
  376. {  return options_readboolx(tag, flag, FALSE);
  377. }
  378.  
  379. static os_error *options__writestr(char *tag, char *buf, BOOL purge)
  380. {  int pos, p2;
  381.    int nlen, olen, tlen, by, sol;
  382.    os_error *err;
  383.    char *tp;
  384.    BOOL ch;
  385.  
  386.    nlen = strlen(buf);
  387.  
  388.    if (pos = options__findtag(tag, &sol), pos >= 0)
  389.    {  ch = FALSE;
  390.  
  391.       if (purge && nlen == 0)
  392.       {  pos = sol;  /* go back to start of line */
  393.          nlen = -1;  /* make by smaller by 1 to get \n too */
  394.          ch = TRUE;
  395.       }
  396.  
  397.       for (p2 = pos, tp = buf;
  398.            !ch && p2 < options__size && options[p2] != '\n' && *tp != '\0';
  399.            p2++, tp++)
  400.          ch = options[p2] != *tp;
  401.  
  402.       if (!ch) ch = (p2 < options__size && options[p2] != '\n') || *tp;
  403.  
  404.       for (p2 = pos, olen = 0; p2 < options__size && options[p2] != '\n'; p2++)
  405.          olen++;
  406.  
  407.       by = nlen - olen;
  408.  
  409.       if (by != 0 && (err = options__fmidextend((void **) &options, by > 0 ? pos : pos - by, by), err))
  410.          return err;
  411.  
  412.       if (nlen > 0) memcpy(options + pos, buf, nlen);
  413.       options__size += nlen - olen;
  414.       
  415.       options__changed |= ch;
  416.    }
  417.    else if (!purge || nlen > 0)
  418.    {  tlen = strlen(tag);
  419.  
  420.       if (!options__taginitchar(*tag))
  421.          goto badtag;
  422.  
  423.       for (tp = tag; *tp; tp++)
  424.          if (!options__tagchar(*tp))
  425.             goto badtag;
  426.  
  427.       if (options)
  428.          err = options__fextend((void **) &options, options__size + nlen + tlen + 2);
  429.       else
  430.          err = options__falloc((void **) &options, nlen + tlen + 2);
  431.  
  432.       memcpy(options + options__size, tag, tlen);
  433.       options[options__size + tlen] = ':';
  434.       memcpy(options + options__size + tlen + 1, buf, nlen);
  435.       options[options__size + nlen + tlen + 1] = '\n';
  436.       options__size += tlen + nlen + 2;
  437.  
  438.       options__changed = TRUE;
  439.    }
  440.  
  441.    return NULL;
  442.  
  443. badtag:
  444.    return options__err("options8:%s is not a legal tag\n", tag);
  445. }
  446.  
  447. os_error *options_writestr(char *tag, char *buf)
  448. {  return options__writestr(tag, buf, FALSE);
  449. }
  450.  
  451. os_error *options_writeif(char *tag, char *buf)
  452. {  return options__writestr(tag, buf, TRUE);
  453. }
  454.  
  455. os_error *options_writeint(char *tag, int num)
  456. {  char nbuf[20];
  457.  
  458.    sprintf(nbuf, "%d", num);
  459.    return options_writestr(tag, nbuf);
  460. }
  461.  
  462. os_error *options_writebool(char *tag, BOOL flag)
  463. {  return options_writestr(tag, flag ? "yes" : "no");
  464. }
  465.  
  466. os_error *options_written(void)
  467. {  os_error *err;
  468.    os_filestr fcb;
  469.    wimp_msgstr msg;
  470.  
  471.    if (!options__changed) return NULL;
  472.  
  473.    fcb.action   = 10;
  474.    fcb.name     = options_NAME;
  475.    fcb.loadaddr = 0xFFF;
  476.    fcb.start    = (int) options;
  477.    fcb.end      = (int) options + options__size;
  478.  
  479.    if (err = os_file(&fcb), err) return err;
  480.  
  481.    options__changed = FALSE;
  482.    options__internal = TRUE;
  483.  
  484.    msg.hdr.action = (wimp_msgaction) intalk_MOPTIONSCHANGED;
  485.    msg.hdr.size   = sizeof(wimp_msghdr);
  486.    msg.hdr.your_ref = 0;
  487.  
  488.    if (err = wimp_sendmessage(wimp_ESEND, &msg, (wimp_t) 0), err)
  489.       return err;
  490.  
  491.    return options__notify();
  492. }
  493.  
  494. static options__watcher *options__unlink(options__watcher *list, options__watcher *cb)
  495. {  if (cb == list)
  496.       return list->link;
  497.  
  498.    list->link = options__unlink(list->link, cb);
  499.    return list;           
  500. }
  501.  
  502. os_error *options_register(options_changedhandler ch, void *handle)
  503. {  options__watcher *cb;
  504.    os_error *err;
  505.  
  506.    if (err = options__malloc((void **) &cb, sizeof(options__watcher)), err) return err;
  507.  
  508.    cb->link   = options__world;
  509.    cb->ch     = ch;
  510.    cb->handle = handle;
  511.  
  512.    options__world = cb;
  513.  
  514.    return NULL;
  515. }
  516.  
  517. os_error *options_unregister(options_changedhandler ch, void *handle)
  518. {  options__watcher *me;
  519.  
  520.    for (me = options__world; me; me = me->link)
  521.       if (me->ch == ch && me->handle == handle)
  522.          goto found;
  523.  
  524.    return NULL;
  525.  
  526. found:
  527.    options__world = options__unlink(options__world, me);
  528.    options__free((void **) &me);
  529.    return NULL;
  530. }
  531.  
  532. os_error *options_set(void)
  533. {  os_regset regs;
  534.  
  535.    regs.r[0] = (int) options_VAR;
  536.    regs.r[1] = NULL;
  537.    regs.r[2] = -1; /* check only */
  538.    regs.r[3] = 0;
  539.    regs.r[4] = 0;
  540.    os_swix(OS_ReadVarVal, ®s);
  541.  
  542.    if (regs.r[2] < 0)
  543.       return wimp_starttask(options_CMD);
  544.  
  545.    return options__err("options10:Can't find !Options");
  546. }
  547.