1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
  
14
  
15
  
16
  
17
  
18
  
19
  
20
  
21
  
22
  
23
  
24
  
25
  
26
  
27
  
28
  
29
  
30
  
31
  
32
  
33
  
34
  
35
  
36
  
37
  
38
  
39
  
40
  
41
  
42
  
43
  
44
  
45
  
46
  
47
  
48
  
49
  
50
  
51
  
52
  
53
  
54
  
55
  
56
  
57
  
58
  
59
  
60
  
61
  
62
  
63
  
64
  
65
  
66
  
67
  
68
  
69
  
70
  
71
  
72
  
73
  
74
  
75
  
76
  
77
  
78
  
79
  
80
  
81
  
82
  
83
  
84
  
85
  
86
  
87
  
88
  
89
  
90
  
91
  
92
  
93
  
94
  
95
  
96
  
97
  
98
  
99
  
100
  
101
  
102
  
103
  
104
  
105
  
106
  
107
  
108
  
109
  
110
  
111
  
112
  
113
  
114
  
115
  
116
  
117
  
118
  
119
  
120
  
121
  
122
  
123
  
124
  
125
  
126
  
127
  
128
  
129
  
130
  
131
  
132
  
133
  
134
  
135
  
136
  
137
  
138
  
139
  
140
  
141
  
142
  
143
  
144
  
145
  
146
  
147
  
#pike __REAL_VERSION__ 
#require constant(System._Inotify.parse_event) 
 
/* vim:syntax=lpc 
 */ 
 
//! @ignore 
inherit System._Inotify; 
//! @endignore 
 
 
//! Turn an event mask into a human readable representation. This is used 
//! for debugging purpose. 
//! 
string describe_mask(int mask) { 
    array(string) list = ({}); 
 
    foreach (sort(indices(this_program));; string name) { 
        if (has_prefix(name, "IN_")) { 
            int value = `[](this_program, name); 
 
            if (value == (value & mask)) list += ({ name }); 
        } 
    } 
 
    return list * "|"; 
} 
 
class Watch { 
    string name; 
    function(int, int, string, mixed ...:void) callback; 
    int mask; 
    array extra; 
 
    void create(string name, int mask, 
                function(int, int, string, mixed ...:void) callback, 
                array extra) { 
        this::name = name; 
        this::mask = mask; 
        this::callback = callback; 
        this::extra = extra; 
    } 
} 
 
class DirWatch { 
    inherit Watch; 
 
    void handle_event(int wd, int mask, int cookie, 
                      int|string name) { 
        if (name) { 
            name = (has_suffix(this::name, "/") 
                    ? this::name : (this::name+"/")) + name; 
        } else { 
            name = this::name; 
        } 
 
        callback(mask, cookie, name, @extra); 
    } 
} 
 
class FileWatch { 
    inherit Watch; 
 
    void handle_event(int wd, int mask, int cookie, 
                      int|string name) { 
        callback(mask, cookie, this::name, @extra); 
    } 
} 
 
//! More convenient interface to inotify(7). Automatically reads events 
//! from the inotify file descriptor and parses them. 
//! 
//! @note 
//!   Objects of this class will be destructed when they go out of external 
//!   references. As such they behave differently from other classes which use 
//!   callbacks, e.g. @[Stdio.File]. 
//! @note 
//!   The number of inotify instances is limited by ulimits. 
class Instance { 
    inherit _Instance; 
    protected mapping(int:object) watches = ([]); 
 
    void event_callback(int wd, int event, int cookie, string path) 
    { 
        Watch watch = watches[wd]; 
        if (watch) { 
            if (event == IN_IGNORED) { 
                m_delete(watches, wd); 
                werror("Watch %O (wd: %d) deleted.\n", watch, wd); 
            } 
            watch->handle_event(wd, event, cookie, path); 
        } 
    } 
 
    void create() { 
        set_event_callback(event_callback); 
        set_nonblocking(); 
    } 
 
     //! Add a watch for a certain file and a set of events specified by 
     //! @expr{mask@}. The function @expr{callback@} will be called when 
     //! such an event occurs. The arguments to the callback will be the 
     //! events mask, the cookie, the @expr{filename@} and @expr{extra@}. 
     //! @returns 
     //!          Returns a watch descriptor which can be used to remove the 
     //!          watch. 
     //! @note 
     //!          When adding a second watch for the same file the old one 
     //!          will be removed unless @[System.Inotify.IN_MASK_ADD] is 
     //!          contained in @expr{mask@}. 
     //! @note 
     //!          The mask of an event may be a subset of the mask given when 
     //!          adding the watch. 
     //! @note 
     //!          In case the watch is added for a regular file, the filename 
     //!          will be passed to the callback. In case the watch is added 
     //!          for a directory, the name of the file to which the event 
     //!          happened inside the directory will be concatenated. 
     //! 
    int add_watch(string filename, int mask, 
                  function(int, int, string, mixed ...:void) callback, 
                  mixed ... extra) 
    { 
        int wd = ::add_watch(filename, mask); 
 
//! @ignore 
# if constant(@module@.IN_MASK_ADD) 
      /* 
         * just in case we want to use the mask in the watches later, 
         * they better be correct 
         */ 
        if ((mask & IN_MASK_ADD) && has_index(watches, wd)) { 
                mask |= watches[wd]->mask; 
        } 
# endif 
//! @endignore 
        if (Stdio.is_dir(filename)) { 
            watches[wd] = DirWatch(filename, mask, callback, extra); 
        } else { 
            watches[wd] = FileWatch(filename, mask, callback, extra); 
        } 
 
        return wd; 
    } 
}