Tidying up #defines:
[privoxy.git] / actions.c
1 const char actions_rcs[] = "$Id: actions.c,v 1.8 2001/06/29 13:19:52 oes Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/actions.c,v $
5  *
6  * Purpose     :  Declares functions to work with actions files
7  *                Functions declared include: FIXME
8  *
9  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
10  *                IJBSWA team.  http://ijbswa.sourceforge.net
11  *
12  *                Based on the Internet Junkbuster originally written
13  *                by and Copyright (C) 1997 Anonymous Coders and 
14  *                Junkbusters Corporation.  http://www.junkbusters.com
15  *
16  *                This program is free software; you can redistribute it 
17  *                and/or modify it under the terms of the GNU General
18  *                Public License as published by the Free Software
19  *                Foundation; either version 2 of the License, or (at
20  *                your option) any later version.
21  *
22  *                This program is distributed in the hope that it will
23  *                be useful, but WITHOUT ANY WARRANTY; without even the
24  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
25  *                PARTICULAR PURPOSE.  See the GNU General Public
26  *                License for more details.
27  *
28  *                The GNU General Public License should be included with
29  *                this file.  If not, you can view it at
30  *                http://www.gnu.org/copyleft/gpl.html
31  *                or write to the Free Software Foundation, Inc., 59
32  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
33  *
34  * Revisions   :
35  *    $Log: actions.c,v $
36  *    Revision 1.8  2001/06/29 13:19:52  oes
37  *    Removed logentry from cancelled commit
38  *
39  *    Revision 1.7  2001/06/09 10:55:28  jongfoster
40  *    Changing BUFSIZ ==> BUFFER_SIZE
41  *
42  *    Revision 1.6  2001/06/07 23:04:34  jongfoster
43  *    Made get_actions() static.
44  *
45  *    Revision 1.5  2001/06/03 19:11:48  oes
46  *    adapted to new enlist_unique arg format
47  *
48  *    Revision 1.4  2001/06/01 20:03:42  jongfoster
49  *    Better memory management - current_action->strings[] now
50  *    contains copies of the strings, not the original.
51  *
52  *    Revision 1.3  2001/06/01 18:49:17  jongfoster
53  *    Replaced "list_share" with "list" - the tiny memory gain was not
54  *    worth the extra complexity.
55  *
56  *    Revision 1.2  2001/05/31 21:40:00  jongfoster
57  *    Removing some commented out, obsolete blocks of code.
58  *
59  *    Revision 1.1  2001/05/31 21:16:46  jongfoster
60  *    Moved functions to process the action list into this new file.
61  *
62  *
63  *********************************************************************/
64 \f
65
66 #include "config.h"
67
68 #include <stdio.h>
69 #include <string.h>
70
71 #include "project.h"
72 #include "jcc.h"
73 #include "list.h"
74 #include "actions.h"
75 #include "miscutil.h"
76 #include "errlog.h"
77 #include "loaders.h"
78
79 const char actions_h_rcs[] = ACTIONS_H_VERSION;
80
81
82 /* Turn off everything except forwarding */
83 /* This structure is used to hold user-defined aliases */
84 struct action_alias
85 {
86    const char * name;
87    struct action_spec action[1];
88    struct action_alias * next;
89 };
90
91
92 /*
93  * Must declare this in this file for the above structure.
94  */
95 static int get_actions (char *line, 
96                         struct action_alias * alias_list,
97                         struct action_spec *cur_action);
98
99 /*
100  * We need the main list of options.
101  *
102  * First, we need a way to tell between boolean, string, and multi-string
103  * options.  For string and multistring options, we also need to be
104  * able to tell the difference between a "+" and a "-".  (For bools,
105  * the "+"/"-" information is encoded in "add" and "mask").  So we use
106  * an enumerated type (well, the preprocessor equivalent).  Here are
107  * the values:
108  */
109 #define AV_NONE       0 /* +opt -opt */
110 #define AV_ADD_STRING 1 /* +stropt{string} */
111 #define AV_REM_STRING 2 /* -stropt */
112 #define AV_ADD_MULTI  3 /* +multiopt{string} +multiopt{string2} */
113 #define AV_REM_MULTI  4 /* -multiopt{string} -multiopt{*}       */
114
115 /*
116  * We need a structure to hold the name, flag changes, 
117  * type, and string index.
118  */
119 struct action_name
120 {
121    const char * name;
122    unsigned mask;   /* a bit set to "0" = remove action */
123    unsigned add;    /* a bit set to "1" = add action */
124    int takes_value; /* an AV_... constant */
125    int index;       /* index into strings[] or multi[] */
126 };
127
128 /*
129  * And with those building blocks in place, here's the array.
130  */
131 static const struct action_name action_names[] =
132 {
133    /*
134     * Well actually there's no data here - it's in actionlist.h
135     * This keeps it together to make it easy to change.
136     *
137     * Here's the macros used to format it:
138     */
139 #define DEFINE_ACTION_MULTI(name,index)                   \
140    { "+" name, ACTION_MASK_ALL, 0, AV_ADD_MULTI, index }, \
141    { "-" name, ACTION_MASK_ALL, 0, AV_REM_MULTI, index },
142 #define DEFINE_ACTION_STRING(name,flag,index)                 \
143    { "+" name, ACTION_MASK_ALL, flag, AV_ADD_STRING, index }, \
144    { "-" name, ~flag, 0, AV_REM_STRING, index },
145 #define DEFINE_ACTION_BOOL(name,flag)   \
146    { "+" name, ACTION_MASK_ALL, flag }, \
147    { "-" name, ~flag, 0 },
148 #define DEFINE_ACTION_ALIAS 1 /* Want aliases please */
149
150 #include "actionlist.h"
151
152 #undef DEFINE_ACTION_MULTI
153 #undef DEFINE_ACTION_STRING
154 #undef DEFINE_ACTION_BOOL
155 #undef DEFINE_ACTION_ALIAS
156
157    { NULL, 0, 0 } /* End marker */
158 };
159
160
161 /*********************************************************************
162  *
163  * Function    :  merge_actions
164  *
165  * Description :  Merge two actions together.
166  *                Similar to "cur_action += new_action".
167  *
168  * Parameters  :
169  *          1  :  cur_action = Current actions, to modify.
170  *          2  :  new_action = Action to add.
171  *
172  * Returns     :  N/A
173  *
174  *********************************************************************/
175 void merge_actions (struct action_spec *dest, 
176                     const struct action_spec *src)
177 {
178    int i;
179
180    dest->mask &= src->mask;
181    dest->add  &= src->mask;
182    dest->add  |= src->add;
183
184    for (i = 0; i < ACTION_STRING_COUNT; i++)
185    {
186       char * str = src->string[i];
187       if (str)
188       {
189          freez(dest->string[i]);
190          dest->string[i] = strdup(str);
191       }
192    }
193
194    for (i = 0; i < ACTION_MULTI_COUNT; i++)
195    {
196       if (src->multi_remove_all[i])
197       {
198          /* Remove everything from dest */
199          destroy_list(dest->multi_remove[i]);
200          destroy_list(dest->multi_add[i]);
201          dest->multi_remove_all[i] = 1;
202          list_duplicate(dest->multi_add[i], src->multi_add[i]);
203       }
204       else if (dest->multi_remove_all[i])
205       {
206          /*
207           * dest already removes everything, so we only need to worry
208           * about what we add.
209           */
210          list_remove_list(dest->multi_add[i], src->multi_remove[i]);
211          list_append_list_unique(dest->multi_add[i], src->multi_add[i]);
212       }
213       else
214       {
215          /* No "remove all"s to worry about. */
216          list_remove_list(dest->multi_add[i], src->multi_remove[i]);
217          list_append_list_unique(dest->multi_remove[i], src->multi_remove[i]);
218          list_append_list_unique(dest->multi_add[i], src->multi_add[i]);
219       }
220    }
221 }
222
223
224 /*********************************************************************
225  *
226  * Function    :  copy_action
227  *
228  * Description :  Copy an action_specs.
229  *                Similar to "cur_action = new_action".
230  *
231  * Parameters  :
232  *          1  :  dest = Destination of copy.
233  *          2  :  src = Source for copy.
234  *
235  * Returns     :  N/A
236  *
237  *********************************************************************/
238 void copy_action (struct action_spec *dest, 
239                   const struct action_spec *src)
240 {
241    int i;
242
243    dest->mask = src->mask;
244    dest->add  = src->add;
245
246    for (i = 0; i < ACTION_STRING_COUNT; i++)
247    {
248       char * str = src->string[i];
249       dest->string[i] = (str ? strdup(str) : NULL);
250    }
251
252    for (i = 0; i < ACTION_MULTI_COUNT; i++)
253    {
254       dest->multi_remove_all[i] = src->multi_remove_all[i];
255       list_duplicate(dest->multi_remove[i], src->multi_remove[i]);
256       list_duplicate(dest->multi_add[i],    src->multi_add[i]);
257    }
258 }
259
260
261 /*********************************************************************
262  *
263  * Function    :  free_action
264  *
265  * Description :  Free an action_specs.
266  *
267  * Parameters  :
268  *          1  :  src = Source to free.
269  *
270  * Returns     :  N/A
271  *
272  *********************************************************************/
273 void free_action (struct action_spec *src)
274 {
275    int i;
276
277    for (i = 0; i < ACTION_STRING_COUNT; i++)
278    {
279       freez(src->string[i]);
280    }
281
282    for (i = 0; i < ACTION_MULTI_COUNT; i++)
283    {
284       destroy_list(src->multi_remove[i]);
285       destroy_list(src->multi_add[i]);
286    }
287
288    memset(src, '\0', sizeof(*src));
289 }
290
291
292 /*********************************************************************
293  *
294  * Function    :  get_action_token
295  *
296  * Description :  Parses a line for the first action.
297  *                Modifies it's input array, doesn't allocate memory.
298  *                e.g. given:
299  *                *line="  +abc{def}  -ghi "
300  *                Returns:
301  *                *line="  -ghi "
302  *                *name="+abc"
303  *                *value="def"
304  *
305  * Parameters  :
306  *          1  :  line = [in] The line containing the action.
307  *                       [out] Start of next action on line, or
308  *                       NULL if we reached the end of line before
309  *                       we found an action.
310  *          2  :  name = [out] Start of action name, null
311  *                       terminated.  NULL on EOL
312  *          3  :  value = [out] Start of action value, null 
313  *                        terminated.  NULL if none or EOL.
314  *
315  * Returns     :  0 => Ok
316  *                nonzero => Mismatched {} (line was trashed anyway)
317  *
318  *********************************************************************/
319 int get_action_token(char **line, char **name, char **value)
320 {
321    char * str = *line;
322    char ch;
323
324    /* set default returns */
325    *line = NULL;
326    *name = NULL;
327    *value = NULL;
328
329    /* Eat any leading whitespace */
330    while ((*str == ' ') || (*str == '\t'))
331    {
332       str++;
333    }
334
335    if (*str == '\0')
336    {
337       return 0;
338    }
339
340    if (*str == '{')
341    {
342       /* null name, just value is prohibited */
343       return 1;
344    }
345
346    *name = str;
347
348    /* parse option */
349    while (((ch = *str) != '\0') && 
350           (ch != ' ') && (ch != '\t') && (ch != '{'))
351    {
352       if (ch == '}')
353       {
354          /* error */
355          return 1;
356       }
357       str++;
358    }
359    *str = '\0';
360
361    if (ch != '{')
362    {
363       /* no value */
364       if (ch == '\0')
365       {
366          /* EOL - be careful not to run off buffer */
367          *line = str;
368       }
369       else
370       {
371          /* More to parse next time. */
372          *line = str + 1;
373       }
374       return 0;
375    }
376
377    str++;
378    *value = str;
379
380    str = strchr(str, '}');
381    if (str == NULL)
382    {
383       /* error */
384       *value = NULL;
385       return 1;
386    }
387
388    /* got value */
389    *str = '\0';
390    *line = str + 1;
391
392    chomp(*value);
393
394    return 0;
395 }
396
397
398 /*********************************************************************
399  *
400  * Function    :  get_actions
401  *
402  * Description :  Parses a list of actions.
403  *
404  * Parameters  :
405  *          1  :  line = The string containing the actions.
406  *                       Will be written to by this function.
407  *          2  :  aliases = Custom alias list, or NULL for none.
408  *          3  :  cur_action = Where to store the action.  Caller
409  *                             allocates memory.
410  *
411  * Returns     :  0 => Ok
412  *                nonzero => Error (line was trashed anyway)
413  *
414  *********************************************************************/
415 static int get_actions(char *line,
416                        struct action_alias * alias_list,
417                        struct action_spec *cur_action)
418 {
419    memset(cur_action, '\0', sizeof(*cur_action));
420    cur_action->mask = ACTION_MASK_ALL;
421
422    while (line)
423    {
424       char * option = NULL;
425       char * value = NULL;
426
427       if (get_action_token(&line, &option, &value))
428       {
429          return 1;
430       }
431
432       if (option)
433       {
434          /* handle option in 'option' */
435       
436          /* Check for standard action name */
437          const struct action_name * action = action_names;
438
439          while ( (action->name != NULL) && (0 != strcmpic(action->name, option)) )
440          {
441             action++;
442          }
443          if (action->name != NULL)
444          {
445             /* Found it */
446             cur_action->mask &= action->mask;
447             cur_action->add  &= action->mask;
448             cur_action->add  |= action->add;
449
450             switch (action->takes_value)
451             {
452             case AV_NONE:
453                /* ignore any option. */
454                break;
455             case AV_ADD_STRING:
456                {
457                   /* add single string. */
458
459                   if ((value == NULL) || (*value == '\0'))
460                   {
461                      return 1;
462                   }
463                   /* FIXME: should validate option string here */
464                   freez (cur_action->string[action->index]);
465                   cur_action->string[action->index] = strdup(value);
466                   break;
467                }
468             case AV_REM_STRING:
469                {
470                   /* remove single string. */
471
472                   freez (cur_action->string[action->index]);
473                   break;
474                }
475             case AV_ADD_MULTI:
476                {
477                   /* append multi string. */
478
479                   struct list * remove = cur_action->multi_remove[action->index];
480                   struct list * add    = cur_action->multi_add[action->index];
481
482                   if ((value == NULL) || (*value == '\0'))
483                   {
484                      return 1;
485                   }
486
487                   list_remove_item(remove, value);
488                   enlist_unique(add, value, 0);
489                   break;
490                }
491             case AV_REM_MULTI:
492                {
493                   /* remove multi string. */
494
495                   struct list * remove = cur_action->multi_remove[action->index];
496                   struct list * add    = cur_action->multi_add[action->index];
497
498                   if ( (value == NULL) || (*value == '\0')
499                      || ((*value == '*') && (value[1] == '\0')) )
500                   {
501                      /*
502                       * no option, or option == "*".
503                       *
504                       * Remove *ALL*.
505                       */
506                      destroy_list(remove);
507                      destroy_list(add);
508                      cur_action->multi_remove_all[action->index] = 1;
509                   }
510                   else
511                   {
512                      /* Valid option - remove only 1 option */
513
514                      if ( !cur_action->multi_remove_all[action->index] )
515                      {
516                         /* there isn't a catch-all in the remove list already */
517                         enlist_unique(remove, value, 0);
518                      }
519                      list_remove_item(add, value);
520                   }
521                   break;
522                }
523             default:
524                /* Shouldn't get here unless there's memory corruption. */
525                return 1;
526             }
527          }
528          else
529          {
530             /* try user aliases. */
531             const struct action_alias * alias = alias_list;
532             
533             while ( (alias != NULL) && (0 != strcmpic(alias->name, option)) )
534             {
535                alias = alias->next;
536             }
537             if (alias != NULL)
538             {
539                /* Found it */
540                merge_actions(cur_action, alias->action);
541             }
542             else
543             {
544                /* Bad action name */
545                return 1;
546             }
547          }
548       }
549    }
550
551    return 0;
552 }
553
554
555 /*********************************************************************
556  *
557  * Function    :  actions_to_text
558  *
559  * Description :  Converts a actionsfile entry from numeric form
560  *                ("mask" and "add") to text.
561  *
562  * Parameters  :
563  *          1  :  mask = As from struct url_actions
564  *          2  :  add  = As from struct url_actions
565  *
566  * Returns     :  A string.  Caller must free it.
567  *
568  *********************************************************************/
569 char * actions_to_text(struct action_spec *action)
570 {
571    unsigned mask = action->mask;
572    unsigned add  = action->add;
573    char * result = strdup("");
574    struct list * lst;
575
576    /* sanity - prevents "-feature +feature" */
577    mask |= add;
578
579
580 #define DEFINE_ACTION_BOOL(__name, __bit)   \
581    if (!(mask & __bit))                     \
582    {                                        \
583       result = strsav(result, " -" __name); \
584    }                                        \
585    else if (add & __bit)                    \
586    {                                        \
587       result = strsav(result, " +" __name); \
588    }
589
590 #define DEFINE_ACTION_STRING(__name, __bit, __index) \
591    if (!(mask & __bit))                     \
592    {                                        \
593       result = strsav(result, " -" __name); \
594    }                                        \
595    else if (add & __bit)                    \
596    {                                        \
597       result = strsav(result, " +" __name "{"); \
598       result = strsav(result, action->string[__index]); \
599       result = strsav(result, "}"); \
600    }
601
602 #define DEFINE_ACTION_MULTI(__name, __index)         \
603    if (action->multi_remove_all[__index])            \
604    {                                                 \
605       result = strsav(result, " -" __name "{*}");    \
606    }                                                 \
607    else                                              \
608    {                                                 \
609       lst = action->multi_remove[__index]->next;     \
610       while (lst)                                    \
611       {                                              \
612          result = strsav(result, " -" __name "{");   \
613          result = strsav(result, lst->str);          \
614          result = strsav(result, "}");               \
615          lst = lst->next;                            \
616       }                                              \
617    }                                                 \
618    lst = action->multi_add[__index]->next;           \
619    while (lst)                                       \
620    {                                                 \
621       result = strsav(result, " +" __name "{");      \
622       result = strsav(result, lst->str);             \
623       result = strsav(result, "}");                  \
624       lst = lst->next;                               \
625    }
626
627 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
628
629 #include "actionlist.h"
630
631 #undef DEFINE_ACTION_MULTI
632 #undef DEFINE_ACTION_STRING
633 #undef DEFINE_ACTION_BOOL
634 #undef DEFINE_ACTION_ALIAS
635
636    return result;
637 }
638
639
640 /*********************************************************************
641  *
642  * Function    :  current_actions_to_text
643  *
644  * Description :  Converts a actionsfile entry to text.
645  *
646  * Parameters  :
647  *          1  :  action = Action
648  *
649  * Returns     :  A string.  Caller must free it.
650  *
651  *********************************************************************/
652 char * current_action_to_text(struct current_action_spec *action)
653 {
654    unsigned flags  = action->flags;
655    char * result = strdup("");
656    struct list * lst;
657
658 #define DEFINE_ACTION_BOOL(__name, __bit)   \
659    if (flags & __bit)                       \
660    {                                        \
661       result = strsav(result, " +" __name); \
662    }                                        \
663    else                                     \
664    {                                        \
665       result = strsav(result, " -" __name); \
666    }
667
668 #define DEFINE_ACTION_STRING(__name, __bit, __index)    \
669    if (flags & __bit)                                   \
670    {                                                    \
671       result = strsav(result, " +" __name "{");         \
672       result = strsav(result, action->string[__index]); \
673       result = strsav(result, "}");                     \
674    }                                                    \
675    else                                                 \
676    {                                                    \
677       result = strsav(result, " -" __name);             \
678    }
679
680 #define DEFINE_ACTION_MULTI(__name, __index)            \
681    lst = action->multi[__index]->next;                  \
682    if (lst == NULL)                                     \
683    {                                                    \
684       result = strsav(result, " -" __name);             \
685    }                                                    \
686    else                                                 \
687    {                                                    \
688       while (lst)                                       \
689       {                                                 \
690          result = strsav(result, " +" __name "{");      \
691          result = strsav(result, lst->str);             \
692          result = strsav(result, "}");                  \
693          lst = lst->next;                               \
694       }                                                 \
695    }
696
697 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
698
699 #include "actionlist.h"
700
701 #undef DEFINE_ACTION_MULTI
702 #undef DEFINE_ACTION_STRING
703 #undef DEFINE_ACTION_BOOL
704 #undef DEFINE_ACTION_ALIAS
705
706    return result;
707 }
708
709
710 /*********************************************************************
711  *
712  * Function    :  init_current_action
713  *
714  * Description :  Zero out an action.
715  *
716  * Parameters  :
717  *          1  :  dest = An uninitialized current_action_spec.
718  *
719  * Returns     :  N/A
720  *
721  *********************************************************************/
722 void init_current_action (struct current_action_spec *dest)
723 {
724    memset(dest, '\0', sizeof(*dest));
725    dest->flags = ACTION_MOST_COMPATIBLE;
726 }
727
728
729 /*********************************************************************
730  *
731  * Function    :  merge_current_action
732  *
733  * Description :  Merge two actions together.
734  *                Similar to "dest += src".
735  *                Differences between this and merge_actions()
736  *                is that this one doesn't allocate memory for
737  *                strings (so "src" better be in memory for at least
738  *                as long as "dest" is, and you'd better free
739  *                "dest" using "current_free_action").
740  *                Also, there is no  mask or remove lists in dest.
741  *                (If we're applying it to a URL, we don't need them)
742  *
743  * Parameters  :
744  *          1  :  dest = Current actions, to modify.
745  *          2  :  src = Action to add.
746  *
747  * Returns     :  N/A
748  *
749  *********************************************************************/
750 void merge_current_action (struct current_action_spec *dest, 
751                            const struct action_spec *src)
752 {
753    int i;
754
755    dest->flags  &= src->mask;
756    dest->flags  |= src->add;
757
758    for (i = 0; i < ACTION_STRING_COUNT; i++)
759    {
760       char * str = src->string[i];
761       if (str)
762       {
763          freez(dest->string[i]);
764          dest->string[i] = strdup(str);
765       }
766    }
767
768    for (i = 0; i < ACTION_MULTI_COUNT; i++)
769    {
770       if (src->multi_remove_all[i])
771       {
772          /* Remove everything from dest */
773          destroy_list(dest->multi[i]);
774          list_duplicate(dest->multi[i], src->multi_add[i]);
775       }
776       else
777       {
778          list_remove_list(dest->multi[i], src->multi_remove[i]);
779          list_append_list_unique(dest->multi[i], src->multi_add[i]);
780       }
781    }
782 }
783
784
785 /*********************************************************************
786  *
787  * Function    :  free_current_action
788  *
789  * Description :  Free a current_action_spec.
790  *
791  * Parameters  :
792  *          1  :  src = Source to free.
793  *
794  * Returns     :  N/A
795  *
796  *********************************************************************/
797 void free_current_action (struct current_action_spec *src)
798 {
799    int i;
800
801    for (i = 0; i < ACTION_STRING_COUNT; i++)
802    {
803       freez(src->string[i]);
804    }
805
806    for (i = 0; i < ACTION_MULTI_COUNT; i++)
807    {
808       destroy_list(src->multi[i]);
809    }
810
811    memset(src, '\0', sizeof(*src));
812 }
813
814
815 /*********************************************************************
816  *
817  * Function    :  unload_actions_file
818  *
819  * Description :  Unloads an actions module.
820  *
821  * Parameters  :
822  *          1  :  file_data = the data structure associated with the
823  *                            actions file.
824  *
825  * Returns     :  N/A
826  *
827  *********************************************************************/
828 void unload_actions_file(void *file_data)
829 {
830    struct url_actions * next;
831    struct url_actions * cur = (struct url_actions *)file_data;
832    while (cur != NULL)
833    {
834       next = cur->next;
835       free_url(cur->url);
836       freez(cur);
837       cur = next;
838    }
839
840 }
841
842
843 /*********************************************************************
844  *
845  * Function    :  load_actions_file
846  *
847  * Description :  Read and parse a action file and add to files
848  *                list.
849  *
850  * Parameters  :
851  *          1  :  csp = Current client state (buffers, headers, etc...)
852  *
853  * Returns     :  0 => Ok, everything else is an error.
854  *
855  *********************************************************************/
856 int load_actions_file(struct client_state *csp)
857 {
858    static struct file_list *current_actions_file  = NULL;
859
860    FILE *fp;
861
862    struct url_actions *last_perm;
863    struct url_actions *perm;
864    char  buf[BUFFER_SIZE];
865    struct file_list *fs;
866 #define MODE_START_OF_FILE 1
867 #define MODE_ACTIONS       2
868 #define MODE_ALIAS         3
869    int mode = MODE_START_OF_FILE;
870    struct action_spec cur_action[1];
871    struct action_alias * alias_list = NULL;
872
873    memset(cur_action, '\0', sizeof(*cur_action));
874
875    if (!check_file_changed(current_actions_file, csp->config->actions_file, &fs))
876    {
877       /* No need to load */
878       if (csp)
879       {
880          csp->actions_list = current_actions_file;
881       }
882       return 0;
883    }
884    if (!fs)
885    {
886       log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': error finding file: %E",
887                 csp->config->actions_file);
888       return 1; /* never get here */
889    }
890
891    fs->f = last_perm = (struct url_actions *)zalloc(sizeof(*last_perm));
892    if (last_perm == NULL)
893    {
894       log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': out of memory!",
895                 csp->config->actions_file);
896       return 1; /* never get here */
897    }
898
899    if ((fp = fopen(csp->config->actions_file, "r")) == NULL)
900    {
901       log_error(LOG_LEVEL_FATAL, "can't load actions file '%s': error opening file: %E",
902                 csp->config->actions_file);
903       return 1; /* never get here */
904    }
905
906    while (read_config_line(buf, sizeof(buf), fp, fs) != NULL)
907    {
908       if (*buf == '{')
909       {
910          /* It's a header block */
911          if (buf[1] == '{')
912          {
913             /* It's {{settings}} or {{alias}} */
914             int len = strlen(buf);
915             char * start = buf + 2;
916             char * end = buf + len - 1;
917             if ((len < 5) || (*end-- != '}') || (*end-- != '}'))
918             {
919                /* too short */
920                fclose(fp);
921                log_error(LOG_LEVEL_FATAL, 
922                   "can't load actions file '%s': invalid line: %s",
923                   csp->config->actions_file, buf);
924                return 1; /* never get here */
925             }
926
927             /* Trim leading and trailing whitespace. */
928             end[1] = '\0';
929             chomp(start);
930
931             if (*start == '\0')
932             {
933                /* too short */
934                fclose(fp);
935                log_error(LOG_LEVEL_FATAL, 
936                   "can't load actions file '%s': invalid line: {{ }}",
937                   csp->config->actions_file);
938                return 1; /* never get here */
939             }
940
941             if (0 == strcmpic(start, "alias"))
942             {
943                /* it's an {{alias}} block */
944
945                mode = MODE_ALIAS;
946             }
947             else
948             {
949                /* invalid {{something}} block */
950                fclose(fp);
951                log_error(LOG_LEVEL_FATAL, 
952                   "can't load actions file '%s': invalid line: {{%s}}",
953                   csp->config->actions_file, start);
954                return 1; /* never get here */
955             }
956          }
957          else
958          {
959             /* It's an actions block */
960
961             char  actions_buf[BUFFER_SIZE];
962             char * end;
963
964             /* set mode */
965             mode    = MODE_ACTIONS;
966
967             /* free old action */
968             free_action(cur_action);
969
970             /* trim { */
971             strcpy(actions_buf, buf + 1);
972
973             /* check we have a trailing } and then trim it */
974             end = actions_buf + strlen(actions_buf) - 1;
975             if (*end != '}')
976             {
977                /* too short */
978                fclose(fp);
979                log_error(LOG_LEVEL_FATAL, 
980                   "can't load actions file '%s': invalid line: %s",
981                   csp->config->actions_file, buf);
982                return 1; /* never get here */
983             }
984             *end = '\0';
985
986             /* trim any whitespace immediately inside {} */
987             chomp(actions_buf);
988
989             if (*actions_buf == '\0')
990             {
991                /* too short */
992                fclose(fp);
993                log_error(LOG_LEVEL_FATAL, 
994                   "can't load actions file '%s': invalid line: %s",
995                   csp->config->actions_file, buf);
996                return 1; /* never get here */
997             }
998
999             if (get_actions(actions_buf, alias_list, cur_action))
1000             {
1001                /* error */
1002                fclose(fp);
1003                log_error(LOG_LEVEL_FATAL, 
1004                   "can't load actions file '%s': invalid line: %s",
1005                   csp->config->actions_file, buf);
1006                return 1; /* never get here */
1007             }
1008          }
1009       }
1010       else if (mode == MODE_ALIAS)
1011       {
1012          /* define an alias */
1013          char  actions_buf[BUFFER_SIZE];
1014          struct action_alias * new_alias;
1015          int more = 1;
1016
1017          char * start = strchr(buf, '=');
1018          char * end = start;
1019
1020          if ((start == NULL) || (start == buf))
1021          {
1022             log_error(LOG_LEVEL_FATAL, 
1023                "can't load actions file '%s': invalid alias line: %s",
1024                csp->config->actions_file, buf);
1025             return 1; /* never get here */
1026          }
1027
1028          if ((new_alias = zalloc(sizeof(*new_alias))) == NULL)
1029          {
1030             fclose(fp);
1031             log_error(LOG_LEVEL_FATAL,
1032                "can't load actions file '%s': out of memory!",
1033                csp->config->actions_file);
1034             return 1; /* never get here */
1035          }
1036
1037          /* Eat any the whitespace before the '=' */
1038          end--;
1039          while ((*end == ' ') || (*end == '\t'))
1040          {
1041             /*
1042              * we already know we must have at least 1 non-ws char
1043              * at start of buf - no need to check
1044              */
1045             end--;
1046          }
1047          end[1] = '\0';
1048
1049          /* Eat any the whitespace after the '=' */
1050          start++;
1051          while ((*start == ' ') || (*start == '\t'))
1052          {
1053             start++;
1054          }
1055          if (*start == '\0')
1056          {
1057             log_error(LOG_LEVEL_FATAL, 
1058                "can't load actions file '%s': invalid alias line: %s",
1059                csp->config->actions_file, buf);
1060             return 1; /* never get here */
1061          }
1062
1063          new_alias->name = strdup(buf);
1064
1065          strcpy(actions_buf, start);
1066
1067          if (get_actions(actions_buf, alias_list, new_alias->action))
1068          {
1069             /* error */
1070             fclose(fp);
1071             log_error(LOG_LEVEL_FATAL, 
1072                "can't load actions file '%s': invalid alias line: %s = %s",
1073                csp->config->actions_file, buf, start);
1074             return 1; /* never get here */
1075          }
1076          
1077          /* add to list */
1078          new_alias->next = alias_list;
1079          alias_list = new_alias;
1080       }
1081       else if (mode == MODE_ACTIONS)
1082       {
1083          /* it's a URL pattern */
1084
1085          /* allocate a new node */
1086          if ((perm = zalloc(sizeof(*perm))) == NULL)
1087          {
1088             fclose(fp);
1089             log_error(LOG_LEVEL_FATAL,
1090                "can't load actions file '%s': out of memory!",
1091                csp->config->actions_file);
1092             return 1; /* never get here */
1093          }
1094
1095          /* Save flags */
1096          copy_action (perm->action, cur_action);
1097
1098          /* Save the URL pattern */
1099          if (create_url_spec(perm->url, buf))
1100          {
1101             fclose(fp);
1102             log_error(LOG_LEVEL_FATAL, 
1103                "can't load actions file '%s': cannot create URL pattern from: %s",
1104                csp->config->actions_file, buf);
1105             return 1; /* never get here */
1106          }
1107
1108          /* add it to the list */
1109          last_perm->next = perm;
1110          last_perm = perm;
1111       }
1112       else if (mode == MODE_START_OF_FILE)
1113       {
1114          /* oops - please have a {} line as 1st line in file. */
1115          fclose(fp);
1116          log_error(LOG_LEVEL_FATAL, 
1117             "can't load actions file '%s': first line is invalid: %s",
1118             csp->config->actions_file, buf);
1119          return 1; /* never get here */
1120       }
1121       else
1122       {
1123          /* How did we get here? This is impossible! */
1124          fclose(fp);
1125          log_error(LOG_LEVEL_FATAL, 
1126             "can't load actions file '%s': INTERNAL ERROR - mode = %d",
1127             csp->config->actions_file, mode);
1128          return 1; /* never get here */
1129       }
1130    }
1131
1132    fclose(fp);
1133    
1134    free_action(cur_action);
1135
1136    while (alias_list != NULL)
1137    {
1138       struct action_alias * next = alias_list->next;
1139       freez((char *)alias_list->name);
1140       free_action(alias_list->action);
1141       free(alias_list);
1142       alias_list = next;
1143    }
1144
1145    /* the old one is now obsolete */
1146    if (current_actions_file)
1147    {
1148       current_actions_file->unloader = unload_actions_file;
1149    }
1150
1151    fs->next    = files->next;
1152    files->next = fs;
1153    current_actions_file = fs;
1154
1155    if (csp)
1156    {
1157       csp->actions_list = fs;
1158    }
1159
1160    return(0);
1161
1162 }