aboutsummaryrefslogtreecommitdiff
path: root/pathologist/src
diff options
context:
space:
mode:
Diffstat (limited to 'pathologist/src')
-rw-r--r--pathologist/src/Makefile.am2
-rw-r--r--pathologist/src/include/Makefile.am9
-rw-r--r--pathologist/src/include/gdbmi.h730
-rw-r--r--pathologist/src/include/monkey_common.h443
-rw-r--r--pathologist/src/include/monkey_getopt_lib.h367
-rw-r--r--pathologist/src/include/mxml.h329
-rw-r--r--pathologist/src/include/platform.h266
-rw-r--r--pathologist/src/include/plibc.h810
-rw-r--r--pathologist/src/mi/Makefile.am29
-rw-r--r--pathologist/src/mi/gdbmi_alloc.c308
-rw-r--r--pathologist/src/mi/gdbmi_breakpoint.c265
-rw-r--r--pathologist/src/mi/gdbmi_connect.c885
-rw-r--r--pathologist/src/mi/gdbmi_data_man.c243
-rw-r--r--pathologist/src/mi/gdbmi_error.c38
-rw-r--r--pathologist/src/mi/gdbmi_get_free_pty.c132
-rw-r--r--pathologist/src/mi/gdbmi_get_free_vt.c156
-rw-r--r--pathologist/src/mi/gdbmi_misc.c118
-rw-r--r--pathologist/src/mi/gdbmi_parse.c1927
-rw-r--r--pathologist/src/mi/gdbmi_prg_control.c598
-rw-r--r--pathologist/src/mi/gdbmi_stack_man.c222
-rw-r--r--pathologist/src/mi/gdbmi_symbol_query.c32
-rw-r--r--pathologist/src/mi/gdbmi_target_man.c119
-rw-r--r--pathologist/src/mi/gdbmi_thread.c89
-rw-r--r--pathologist/src/mi/gdbmi_var_obj.c371
-rw-r--r--pathologist/src/minixml/Makefile.am27
-rw-r--r--pathologist/src/minixml/config.h95
-rw-r--r--pathologist/src/minixml/mxml-attr.c319
-rw-r--r--pathologist/src/minixml/mxml-entity.c460
-rw-r--r--pathologist/src/minixml/mxml-file.c3080
-rw-r--r--pathologist/src/minixml/mxml-get.c471
-rw-r--r--pathologist/src/minixml/mxml-index.c662
-rw-r--r--pathologist/src/minixml/mxml-node.c807
-rw-r--r--pathologist/src/minixml/mxml-private.c331
-rw-r--r--pathologist/src/minixml/mxml-private.h50
-rw-r--r--pathologist/src/minixml/mxml-search.c287
-rw-r--r--pathologist/src/minixml/mxml-set.c349
-rw-r--r--pathologist/src/minixml/mxml-string.c476
-rw-r--r--pathologist/src/minixml/mxmldoc.c5809
-rw-r--r--pathologist/src/pathologist/Makefile.am58
-rw-r--r--pathologist/src/pathologist/action.c1124
-rw-r--r--pathologist/src/pathologist/action.h100
-rw-r--r--pathologist/src/pathologist/edb_api.c368
-rw-r--r--pathologist/src/pathologist/getopt.c1047
-rw-r--r--pathologist/src/pathologist/getopt_helpers.c294
-rw-r--r--pathologist/src/pathologist/mail_sender.c231
-rw-r--r--pathologist/src/pathologist/pathologist.c357
-rw-r--r--pathologist/src/pathologist/pathologist_edb.h158
-rw-r--r--pathologist/src/pathologist/pathologist_xml_writer.h54
-rw-r--r--pathologist/src/pathologist/test_monkey_assertion_failure.sh16
-rw-r--r--pathologist/src/pathologist/test_monkey_bad_memory_access.sh15
-rw-r--r--pathologist/src/pathologist/test_monkey_data.conf43
-rwxr-xr-xpathologist/src/pathologist/test_monkey_npe.sh13
-rw-r--r--pathologist/src/pathologist/test_pathologist.c412
-rw-r--r--pathologist/src/pathologist/xml_writer.c126
-rw-r--r--pathologist/src/struct_parse/exmpl.c31
-rw-r--r--pathologist/src/struct_parse/p.c147
-rw-r--r--pathologist/src/struct_parse/test.sh2
-rw-r--r--pathologist/src/struct_parse/test.txt1
-rw-r--r--pathologist/src/struct_parse/test1.txt1
-rw-r--r--pathologist/src/struct_parse/test2.txt1
-rw-r--r--pathologist/src/struct_parse/test3.txt1
-rw-r--r--pathologist/src/struct_parse/test4.txt1
-rw-r--r--pathologist/src/util/Makefile.am12
-rw-r--r--pathologist/src/util/common_allocation.c338
-rw-r--r--pathologist/src/util/str_replace_all.c56
65 files changed, 26718 insertions, 0 deletions
diff --git a/pathologist/src/Makefile.am b/pathologist/src/Makefile.am
new file mode 100644
index 0000000..d2b35f4
--- /dev/null
+++ b/pathologist/src/Makefile.am
@@ -0,0 +1,2 @@
1SUBDIRS = include mi util minixml pathologist .
2
diff --git a/pathologist/src/include/Makefile.am b/pathologist/src/include/Makefile.am
new file mode 100644
index 0000000..b301e48
--- /dev/null
+++ b/pathologist/src/include/Makefile.am
@@ -0,0 +1,9 @@
1SUBDIRS = .
2
3monkeyincludedir = $(includedir)/monkey
4
5monkeyinclude_HEADERS = \
6 monkey_common.h \
7 plibc.h \
8 gdbmi.h \
9 mxml.h
diff --git a/pathologist/src/include/gdbmi.h b/pathologist/src/include/gdbmi.h
new file mode 100644
index 0000000..588c50b
--- /dev/null
+++ b/pathologist/src/include/gdbmi.h
@@ -0,0 +1,730 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004-2009 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Comments:
7 Main header for libmigdb.
8
9***************************************************************************/
10
11#ifndef GDBMI_H
12#define GDBMI_H
13
14#ifdef __cplusplus
15extern "C"
16{
17#if 0 /* keep Emacsens' auto-indent happy */
18}
19#endif
20#endif
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <unistd.h> /* pid_t */
25
26#define MI_OK 0
27#define MI_OUT_OF_MEMORY 1
28#define MI_PIPE_CREATE 2
29#define MI_FORK 3
30#define MI_DEBUGGER_RUN 4
31#define MI_PARSER 5
32#define MI_UNKNOWN_ASYNC 6
33#define MI_UNKNOWN_RESULT 7
34#define MI_FROM_GDB 8
35#define MI_GDB_TIME_OUT 9
36#define MI_GDB_DIED 10
37#define MI_MISSING_XTERM 11
38#define MI_CREATE_TEMPORAL 12
39#define MI_MISSING_GDB 13
40#define MI_LAST_ERROR 13
41
42#define MI_R_NONE 0 /* We are no waiting any response. */
43#define MI_R_SKIP 1 /* We want to discard it. */
44#define MI_R_FE_AND_S 2 /* Wait for done. */
45#define MI_R_E_ARGS 3
46
47enum mi_val_type { t_const, t_tuple, t_list };
48
49/* Types and subtypes. */
50/* Type. */
51#define MI_T_OUT_OF_BAND 0
52#define MI_T_RESULT_RECORD 1
53/* Out of band subtypes. */
54#define MI_ST_ASYNC 0
55#define MI_ST_STREAM 1
56/* Async sub-subtypes. */
57#define MI_SST_EXEC 0
58#define MI_SST_STATUS 1
59#define MI_SST_NOTIFY 2
60/* Stream sub-subtypes. */
61#define MI_SST_CONSOLE 3
62#define MI_SST_TARGET 4
63#define MI_SST_LOG 5
64/* Classes. */
65/* Async classes. */
66#define MI_CL_UNKNOWN 0
67#define MI_CL_STOPPED 1
68#define MI_CL_DOWNLOAD 2
69/* Result classes. */
70#define MI_CL_DONE 2
71#define MI_CL_RUNNING 3
72#define MI_CL_CONNECTED 4
73#define MI_CL_ERROR 5
74#define MI_CL_EXIT 6
75
76#define MI_DEFAULT_TIME_OUT 10
77
78#define MI_DIS_ASM 0
79#define MI_DIS_SRC_ASM 1
80
81/* Implemented workaround for gdb bugs that we can dis/enable. */
82/* At least gdb<=6.1.1 fails to find a source file with absolute path if the
83 name is for a psym instead of a sym. psym==partially loaded symbol table. */
84#define MI_PSYM_SEARCH 0
85
86#define MI_VERSION_STR "0.8.12"
87#define MI_VERSION_MAJOR 0
88#define MI_VERSION_MIDDLE 8
89#define MI_VERSION_MINOR 12
90
91struct mi_results_struct
92{
93 char *var; /* Result name or NULL if just a value. */
94 enum mi_val_type type;
95 union
96 {
97 char *cstr;
98 struct mi_results_struct *rs;
99 } v;
100 struct mi_results_struct *next;
101};
102typedef struct mi_results_struct mi_results;
103
104struct mi_output_struct
105{
106 /* Type of output. */
107 char type;
108 char stype;
109 char sstype;
110 char tclass;
111 /* Content. */
112 mi_results *c;
113 /* Always modeled as a list. */
114 struct mi_output_struct *next;
115};
116typedef struct mi_output_struct mi_output;
117
118typedef void (*stream_cb)(const char *, void *);
119typedef void (*async_cb)(mi_output *o, void *);
120typedef int (*tm_cb)(void *);
121
122/* Values of this structure shouldn't be manipulated by the user. */
123struct mi_h_struct
124{
125 /* Pipes connected to gdb. */
126 int to_gdb[2];
127 int from_gdb[2];
128 /* Streams for the pipes. */
129 FILE *to, *from;
130 /* PID of child gdb. */
131 pid_t pid;
132 char died;
133 /* Which rensponse we are waiting for. */
134 /*int response;*/
135 /* The line we are reading. */
136 char *line;
137 int llen, lread;
138 /* Parsed output. */
139 mi_output *po, *last;
140 /* Tunneled streams callbacks. */
141 stream_cb console;
142 void *console_data;
143 stream_cb target;
144 void *target_data;
145 stream_cb log;
146 void *log_data;
147 /* Async responses callback. */
148 async_cb async;
149 void *async_data;
150 /* Callbacks to get echo of gdb dialog. */
151 stream_cb to_gdb_echo;
152 void *to_gdb_echo_data;
153 stream_cb from_gdb_echo;
154 void *from_gdb_echo_data;
155 /* Time out */
156 tm_cb time_out_cb;
157 void *time_out_cb_data;
158 int time_out;
159 /* Ugly workaround for some of the show responses :-( */
160 int catch_console;
161 char *catched_console;
162 /* MI version, currently unknown but the user can force v2 */
163 unsigned version;
164};
165typedef struct mi_h_struct mi_h;
166
167#define MI_TO(a) ((a)->to_gdb[1])
168
169enum mi_bkp_type { t_unknown=0, t_breakpoint=1, t_hw=2 };
170enum mi_bkp_disp { d_unknown=0, d_keep=1, d_del=2 };
171enum mi_bkp_mode { m_file_line=0, m_function=1, m_file_function=2, m_address=3 };
172
173struct mi_bkpt_struct
174{
175 int number;
176 enum mi_bkp_type type;
177 enum mi_bkp_disp disp; /* keep or del if temporal */
178 char enabled;
179 void *addr;
180 char *func;
181 char *file;
182 int line;
183 int ignore;
184 int times;
185
186 /* For the user: */
187 char *cond;
188 char *file_abs;
189 int thread;
190 enum mi_bkp_mode mode;
191 struct mi_bkpt_struct *next;
192};
193typedef struct mi_bkpt_struct mi_bkpt;
194
195enum mi_wp_mode { wm_unknown=0, wm_write=1, wm_read=2, wm_rw=3 };
196
197struct mi_wp_struct
198{
199 int number;
200 char *exp;
201 enum mi_wp_mode mode;
202
203 /* For the user: */
204 struct mi_wp_struct *next;
205 char enabled;
206};
207typedef struct mi_wp_struct mi_wp;
208
209struct mi_frames_struct
210{
211 int level; /* The frame number, 0 being the topmost frame, i.e. the innermost
212 function. */
213 void *addr; /* The `$pc' value for that frame. */
214 char *func; /* Function name. */
215 char *file; /* File name of the source file where the function lives. */
216 char *from;
217 int line; /* Line number corresponding to the `$pc'. */
218 /* When arguments are available: */
219 mi_results *args;
220 int thread_id;
221 /* When more than one is provided: */
222 struct mi_frames_struct *next;
223};
224typedef struct mi_frames_struct mi_frames;
225
226struct mi_aux_term_struct
227{
228 pid_t pid;
229 char *tty;
230};
231typedef struct mi_aux_term_struct mi_aux_term;
232
233struct mi_pty_struct
234{
235 char *slave;
236 int master;
237};
238typedef struct mi_pty_struct mi_pty;
239
240enum mi_gvar_fmt { fm_natural=0, fm_binary=1, fm_decimal=2, fm_hexadecimal=3,
241 fm_octal=4,
242 /* Only for registers format: */
243 fm_raw=5 };
244enum mi_gvar_lang { lg_unknown=0, lg_c, lg_cpp, lg_java };
245
246#define MI_ATTR_DONT_KNOW 0
247#define MI_ATTR_NONEDITABLE 1
248#define MI_ATTR_EDITABLE 2
249
250struct mi_gvar_struct
251{
252 char *name;
253 int numchild;
254 char *type;
255 enum mi_gvar_fmt format;
256 enum mi_gvar_lang lang;
257 char *exp;
258 int attr;
259
260 /* MI v2 fills it, not yet implemented here. */
261 /* Use gmi_var_evaluate_expression. */
262 char *value;
263
264 /* Pointer to the parent. NULL if none. */
265 struct mi_gvar_struct *parent;
266 /* List containing the children.
267 Filled by gmi_var_list_children.
268 NULL if numchild==0 or not yet filled. */
269 struct mi_gvar_struct *child;
270 /* Next var in the list. */
271 struct mi_gvar_struct *next;
272
273 /* For the user: */
274 char opened; /* We will show its children. 1 when we fill "child" */
275 char changed; /* Needs to be updated. 0 when created. */
276 int vischild; /* How many items visible. numchild when we fill "child" */
277 int depth; /* How deep is this var. */
278 char ispointer;
279};
280typedef struct mi_gvar_struct mi_gvar;
281
282struct mi_gvar_chg_struct
283{
284 char *name;
285 int in_scope; /* if true the other fields apply. */
286 char *new_type; /* NULL if type_changed==false */
287 int new_num_children; /* only when new_type!=NULL */
288
289 struct mi_gvar_chg_struct *next;
290};
291typedef struct mi_gvar_chg_struct mi_gvar_chg;
292
293
294/* A list of assembler instructions. */
295struct mi_asm_insn_struct
296{
297 void *addr;
298 char *func;
299 unsigned offset;
300 char *inst;
301
302 struct mi_asm_insn_struct *next;
303};
304typedef struct mi_asm_insn_struct mi_asm_insn;
305
306/* A list of source lines containing assembler instructions. */
307struct mi_asm_insns_struct
308{
309 char *file;
310 int line;
311 mi_asm_insn *ins;
312
313 struct mi_asm_insns_struct *next;
314};
315typedef struct mi_asm_insns_struct mi_asm_insns;
316
317/* Changed register. */
318struct mi_chg_reg_struct
319{
320 int reg;
321 char *val;
322 char *name;
323 char updated;
324
325 struct mi_chg_reg_struct *next;
326};
327typedef struct mi_chg_reg_struct mi_chg_reg;
328
329/*
330 Examining gdb sources and looking at docs I can see the following "stop"
331reasons:
332
333Breakpoints:
334a) breakpoint-hit (bkptno) + frame
335Also: without reason for temporal breakpoints.
336
337Watchpoints:
338b) watchpoint-trigger (wpt={number,exp};value={old,new}) + frame
339c) read-watchpoint-trigger (hw-rwpt{number,exp};value={value}) + frame
340d) access-watchpoint-trigger (hw-awpt{number,exp};value={[old,]new}) + frame
341e) watchpoint-scope (wpnum) + frame
342
343Movement:
344f) function-finished ([gdb-result-var,return-value]) + frame
345g) location-reached + frame
346h) end-stepping-range + frame
347
348Exit:
349i) exited-signalled (signal-name,signal-meaning)
350j) exited (exit-code)
351k) exited-normally
352
353Signal:
354l) signal-received (signal-name,signal-meaning) + frame
355
356Plus: thread-id
357*/
358enum mi_stop_reason
359{
360 sr_unknown=0,
361 sr_bkpt_hit,
362 sr_wp_trigger, sr_read_wp_trigger, sr_access_wp_trigger, sr_wp_scope,
363 sr_function_finished, sr_location_reached, sr_end_stepping_range,
364 sr_exited_signalled, sr_exited, sr_exited_normally,
365 sr_signal_received
366};
367
368struct mi_stop_struct
369{
370 enum mi_stop_reason reason; /* If more than one reason just the last. */
371 /* Flags indicating if non-pointer fields are filled. */
372 char have_thread_id;
373 char have_bkptno;
374 char have_exit_code;
375 char have_wpno;
376 /* Where stopped. Doesn't exist for sr_exited*. */
377 int thread_id;
378 mi_frames *frame;
379 /* sr_bkpt_hit */
380 int bkptno;
381 /* sr_*wp_* no scope */
382 mi_wp *wp;
383 char *wp_old;
384 char *wp_val;
385 /* sr_wp_scope */
386 int wpno;
387 /* sr_function_finished. Not for void func. */
388 char *gdb_result_var;
389 char *return_value;
390 /* sr_exited_signalled, sr_signal_received */
391 char *signal_name;
392 char *signal_meaning;
393 /* sr_exited */
394 int exit_code;
395};
396typedef struct mi_stop_struct mi_stop;
397
398/* Variable containing the last error. */
399extern int mi_error;
400extern char *mi_error_from_gdb;
401const char *mi_get_error_str();
402
403/* Indicate the name of gdb exe. Default is /usr/bin/gdb */
404void mi_set_gdb_exe(const char *name);
405const char *mi_get_gdb_exe();
406/* Indicate the name of a file containing commands to send at start-up */
407void mi_set_gdb_start(const char *name);
408const char *mi_get_gdb_start();
409/* Indicate the name of a file containing commands to send after connection */
410void mi_set_gdb_conn(const char *name);
411const char *mi_get_gdb_conn();
412void mi_send_target_commands(mi_h *h);
413/* Connect to a local copy of gdb. */
414mi_h *mi_connect_local();
415/* Close connection. You should ask gdb to quit first. */
416void mi_disconnect(mi_h *h);
417/* Force MI version. */
418#define MI_VERSION2U(maj,mid,min) (maj*0x1000000+mid*0x10000+min)
419void mi_force_version(mi_h *h, unsigned vMajor, unsigned vMiddle,
420 unsigned vMinor);
421void mi_set_workaround(unsigned wa, int enable);
422int mi_get_workaround(unsigned wa);
423/* Parse gdb output. */
424mi_output *mi_parse_gdb_output(const char *str);
425/* Functions to set/get the tunneled streams callbacks. */
426void mi_set_console_cb(mi_h *h, stream_cb cb, void *data);
427void mi_set_target_cb(mi_h *h, stream_cb cb, void *data);
428void mi_set_log_cb(mi_h *h, stream_cb cb, void *data);
429stream_cb mi_get_console_cb(mi_h *h, void **data);
430stream_cb mi_get_target_cb(mi_h *h, void **data);
431stream_cb mi_get_log_cb(mi_h *h, void **data);
432/* The callback to deal with async events. */
433void mi_set_async_cb(mi_h *h, async_cb cb, void *data);
434async_cb mi_get_async_cb(mi_h *h, void **data);
435/* Time out in gdb responses. */
436void mi_set_time_out_cb(mi_h *h, tm_cb cb, void *data);
437tm_cb mi_get_time_out_cb(mi_h *h, void **data);
438void mi_set_time_out(mi_h *h, int to);
439int mi_get_time_out(mi_h *h);
440/* Callbacks to "see" the dialog with gdb. */
441void mi_set_to_gdb_cb(mi_h *h, stream_cb cb, void *data);
442void mi_set_from_gdb_cb(mi_h *h, stream_cb cb, void *data);
443stream_cb mi_get_to_gdb_cb(mi_h *h, void **data);
444stream_cb mi_get_from_gdb_cb(mi_h *h, void **data);
445/* Sends a message to gdb. */
446int mi_send(mi_h *h, const char *format, ...);
447/* Wait until gdb sends a response. */
448mi_output *mi_get_response_blk(mi_h *h);
449/* Check if gdb sent a complete response. Use with mi_retire_response. */
450int mi_get_response(mi_h *h);
451/* Get the last response. Use with mi_get_response. */
452mi_output *mi_retire_response(mi_h *h);
453/* Look for a result record in gdb output. */
454mi_output *mi_get_rrecord(mi_output *r);
455/* Look if the output contains an async stop.
456 If that's the case return the reason for the stop.
457 If the output contains an error the description is returned in reason. */
458int mi_get_async_stop_reason(mi_output *r, char **reason);
459mi_stop *mi_get_stopped(mi_results *r);
460mi_frames *mi_get_async_frame(mi_output *r);
461/* Wait until gdb sends a response.
462 Then check if the response is of the desired type. */
463int mi_res_simple_exit(mi_h *h);
464int mi_res_simple_done(mi_h *h);
465int mi_res_simple_running(mi_h *h);
466int mi_res_simple_connected(mi_h *h);
467/* It additionally extracts an specified variable. */
468mi_results *mi_res_done_var(mi_h *h, const char *var);
469/* Extract a frames list from the response. */
470mi_frames *mi_res_frames_array(mi_h *h, const char *var);
471mi_frames *mi_res_frames_list(mi_h *h);
472mi_frames *mi_parse_frame(mi_results *c);
473mi_frames *mi_res_frame(mi_h *h);
474/* Create an auxiliar terminal using xterm. */
475mi_aux_term *gmi_start_xterm();
476/* Indicate the name of xterm exe. Default is /usr/bin/X11/xterm */
477void mi_set_xterm_exe(const char *name);
478const char *mi_get_xterm_exe();
479/* Kill the auxiliar terminal and release the structure. */
480void gmi_end_aux_term(mi_aux_term *t);
481/* Look for a free Linux VT for the child. */
482mi_aux_term *gmi_look_for_free_vt();
483/* Look for a free and usable Linux VT. */
484int mi_look_for_free_vt();
485/* Close master and release the structure. */
486void gmi_end_pty(mi_pty *p);
487/* Look for a free pseudo terminal. */
488mi_pty *gmi_look_for_free_pty();
489/* Extract a list of thread IDs from response. */
490int mi_res_thread_ids(mi_h *h, int **list);
491int mi_get_thread_ids(mi_output *res, int **list);
492/* A variable response. */
493mi_gvar *mi_res_gvar(mi_h *h, mi_gvar *cur, const char *expression);
494enum mi_gvar_fmt mi_format_str_to_enum(const char *format);
495const char *mi_format_enum_to_str(enum mi_gvar_fmt format);
496char mi_format_enum_to_char(enum mi_gvar_fmt format);
497enum mi_gvar_lang mi_lang_str_to_enum(const char *lang);
498const char *mi_lang_enum_to_str(enum mi_gvar_lang lang);
499int mi_res_changelist(mi_h *h, mi_gvar_chg **changed);
500int mi_res_children(mi_h *h, mi_gvar *v);
501mi_bkpt *mi_res_bkpt(mi_h *h);
502mi_wp *mi_res_wp(mi_h *h);
503char *mi_res_value(mi_h *h);
504mi_stop *mi_res_stop(mi_h *h);
505enum mi_stop_reason mi_reason_str_to_enum(const char *s);
506const char *mi_reason_enum_to_str(enum mi_stop_reason r);
507int mi_get_read_memory(mi_h *h, unsigned char *dest, unsigned ws, int *na,
508 unsigned long *addr);
509mi_asm_insns *mi_get_asm_insns(mi_h *h);
510/* Starting point of the program. */
511void mi_set_main_func(const char *name);
512const char *mi_get_main_func();
513mi_chg_reg *mi_get_list_registers(mi_h *h, int *how_many);
514int mi_get_list_registers_l(mi_h *h, mi_chg_reg *l);
515mi_chg_reg *mi_get_list_changed_regs(mi_h *h);
516int mi_get_reg_values(mi_h *h, mi_chg_reg *l);
517mi_chg_reg *mi_get_reg_values_l(mi_h *h, int *how_many);
518int gmi_target_download(mi_h *h);
519
520/* Allocation functions: */
521void *mi_calloc(size_t count, size_t sz);
522void *mi_calloc1(size_t sz);
523char *mi_malloc(size_t sz);
524mi_results *mi_alloc_results(void);
525mi_output *mi_alloc_output(void);
526mi_frames *mi_alloc_frames(void);
527mi_gvar *mi_alloc_gvar(void);
528mi_gvar_chg *mi_alloc_gvar_chg(void);
529mi_bkpt *mi_alloc_bkpt(void);
530mi_wp *mi_alloc_wp(void);
531mi_stop *mi_alloc_stop(void);
532mi_asm_insns *mi_alloc_asm_insns(void);
533mi_asm_insn *mi_alloc_asm_insn(void);
534mi_chg_reg *mi_alloc_chg_reg(void);
535void mi_free_output(mi_output *r);
536void mi_free_output_but(mi_output *r, mi_output *no, mi_results *no_r);
537void mi_free_frames(mi_frames *f);
538void mi_free_aux_term(mi_aux_term *t);
539void mi_free_results(mi_results *r);
540void mi_free_results_but(mi_results *r, mi_results *no);
541void mi_free_gvar(mi_gvar *v);
542void mi_free_gvar_chg(mi_gvar_chg *p);
543void mi_free_wp(mi_wp *wp);
544void mi_free_stop(mi_stop *s);
545void mi_free_asm_insns(mi_asm_insns *i);
546void mi_free_asm_insn(mi_asm_insn *i);
547void mi_free_charp_list(char **l);
548void mi_free_chg_reg(mi_chg_reg *r);
549
550/* Porgram control: */
551/* Specify the executable and arguments for local debug. */
552int gmi_set_unwind_on_signal_on(mi_h * h);
553int gmi_set_exec(mi_h *h, const char *file, const char *args);
554/* Start running the executable. Remote sessions starts running. */
555int gmi_exec_run(mi_h *h);
556/* Continue the execution after a "stop". */
557int gmi_exec_continue(mi_h *h);
558/* Continue the execution in reverse order after a "stop". */
559int gmi_exec_continue_reverse(mi_h *h);
560/* Indicate which terminal will use the target program. For local sessions. */
561int gmi_target_terminal(mi_h *h, const char *tty_name);
562/* Specify what's the local copy that have debug info. For remote sessions. */
563int gmi_file_symbol_file(mi_h *h, const char *file);
564/* Continue until function return, the return value is included in the async
565 response. */
566int gmi_exec_finish(mi_h *h);
567/* Continue backwardly until function return, the return value is included in
568 the async response.*/
569int gmi_exec_finish_reverse(mi_h *h);
570/* Stop the program using SIGINT. */
571int gmi_exec_interrupt(mi_h *h);
572/* Next line of code. */
573int gmi_exec_next(mi_h *h);
574/* Start recording a process */
575int gmi_exec_record_process(mi_h *h);
576/* Stop recording a process */
577int gmi_exec_record_stop(mi_h *h);
578/* Next count lines of code. */
579int gmi_exec_next_cnt(mi_h *h, int count);
580/* Next line of assembler code. */
581int gmi_exec_next_instruction(mi_h *h);
582/* Next line of code. Get inside functions. */
583int gmi_exec_step(mi_h *h);
584/* Previous line of code. Get inside functions. */
585int gmi_exec_step_back(mi_h *h);
586/* Previous line of code (do not step into).*/
587int gmi_exec_next_reverse(mi_h *h);
588/* Next count lines of code. Get inside functions. */
589int gmi_exec_step_cnt(mi_h *h, int count);
590/* Next line of assembler code. Get inside calls. */
591int gmi_exec_step_instruction(mi_h *h);
592/* Execute until location is reached. If file is NULL then is until next line. */
593int gmi_exec_until(mi_h *h, const char *file, int line);
594int gmi_exec_until_addr(mi_h *h, void *addr);
595/* Return to previous frame inmediatly. */
596mi_frames *gmi_exec_return(mi_h *h);
597/* Just kill the program. Please read the notes in prg_control.c. */
598int gmi_exec_kill(mi_h *h);
599
600/* Target manipulation: */
601/* Connect to a remote gdbserver using the specified methode. */
602int gmi_target_select(mi_h *h, const char *type, const char *params);
603/* Attach to an already running process. */
604mi_frames *gmi_target_attach(mi_h *h, pid_t pid);
605/* Detach from an attached process. */
606int gmi_target_detach(mi_h *h);
607
608/* Miscellaneous commands: */
609/* Exit gdb killing the child is it is running. */
610void gmi_gdb_exit(mi_h *h);
611/* Send the version to the console. */
612int gmi_gdb_version(mi_h *h);
613/* Set a gdb variable. */
614int gmi_gdb_set(mi_h *h, const char *var, const char *val);
615/* Get a gdb variable. */
616char *gmi_gdb_show(mi_h *h, const char *var);
617
618/* Breakpoints manipulation: */
619/* Insert a breakpoint at file:line. */
620mi_bkpt *gmi_break_insert(mi_h *h, const char *file, int line);
621/* Insert a breakpoint, all available options. */
622mi_bkpt *gmi_break_insert_full(mi_h *h, int temporary, int hard_assist,
623 const char *cond, int count, int thread,
624 const char *where);
625mi_bkpt *gmi_break_insert_full_fl(mi_h *h, const char *file, int line,
626 int temporary, int hard_assist,
627 const char *cond, int count, int thread);
628/* Remove a breakpoint. */
629int gmi_break_delete(mi_h *h, int number);
630/* Free the memory used for a breakpoint description. */
631void mi_free_bkpt(mi_bkpt *b);
632/* Modify the "ignore" count for a breakpoint. */
633int gmi_break_set_times(mi_h *h, int number, int count);
634/* Associate a condition with the breakpoint. */
635int gmi_break_set_condition(mi_h *h, int number, const char *condition);
636/* Enable or disable a breakpoint. */
637int gmi_break_state(mi_h *h, int number, int enable);
638/* Set a watchpoint. It doesn't work for remote targets! */
639mi_wp *gmi_break_watch(mi_h *h, enum mi_wp_mode mode, const char *exp);
640
641/* Data Manipulation. */
642/* Evaluate an expression. Returns a parsed tree. */
643char *gmi_data_evaluate_expression(mi_h *h, const char *expression);
644/* Path for sources. */
645int gmi_dir(mi_h *h, const char *path);
646/* A very limited "data read memory" implementation. */
647int gmi_read_memory(mi_h *h, const char *exp, unsigned size,
648 unsigned char *dest, int *na, int convAddr,
649 unsigned long *addr);
650mi_asm_insns *gmi_data_disassemble_se(mi_h *h, const char *start,
651 const char *end, int mode);
652mi_asm_insns *gmi_data_disassemble_fl(mi_h *h, const char *file, int line,
653 int lines, int mode);
654mi_chg_reg *gmi_data_list_register_names(mi_h *h, int *how_many);
655int gmi_data_list_register_names_l(mi_h *h, mi_chg_reg *l);
656mi_chg_reg *gmi_data_list_changed_registers(mi_h *h);
657int gmi_data_list_register_values(mi_h *h, enum mi_gvar_fmt fmt, mi_chg_reg *l);
658mi_chg_reg *gmi_data_list_all_register_values(mi_h *h, enum mi_gvar_fmt fmt, int *how_many);
659
660/* Stack manipulation. */
661/* List of frames. Arguments aren't filled. */
662mi_frames *gmi_stack_list_frames(mi_h *h);
663/* List of frames. Indicating a range. */
664mi_frames *gmi_stack_list_frames_r(mi_h *h, int from, int to);
665/* List arguments. Only level and args filled. */
666mi_frames *gmi_stack_list_arguments(mi_h *h, int show);
667/* List arguments. Indicating a range. Only level and args filled. */
668mi_frames *gmi_stack_list_arguments_r(mi_h *h, int show, int from, int to);
669/* Information about the current frame, including args. */
670mi_frames *gmi_stack_info_frame(mi_h *h);
671/* Stack info depth. error => -1 */
672int gmi_stack_info_depth_get(mi_h *h);
673/* Set stack info depth. error => -1 */
674int gmi_stack_info_depth(mi_h *h, int max_depth);
675/* Change current frame. */
676int gmi_stack_select_frame(mi_h *h, int framenum);
677/* List of local vars. */
678mi_results *gmi_stack_list_locals(mi_h *h, int show);
679
680/* Thread. */
681/* List available thread ids. */
682int gmi_thread_list_ids(mi_h *h, int **list);
683/* Select a thread. */
684mi_frames *gmi_thread_select(mi_h *h, int id);
685/* List available threads. */
686mi_frames *gmi_thread_list_all_threads(mi_h *h);
687
688/* Variable objects. */
689/* Create a variable object. */
690mi_gvar *gmi_var_create_nm(mi_h *h, const char *name, int frame, const char *exp);
691mi_gvar *gmi_var_create(mi_h *h, int frame, const char *exp);
692/* Create the variable and also fill the lang and attr fields. */
693mi_gvar *gmi_full_var_create(mi_h *h, int frame, const char *exp);
694/* Delete a variable object. Doesn't free the mi_gvar data. */
695int gmi_var_delete(mi_h *h, mi_gvar *var);
696/* Set the format used to represent the result. */
697int gmi_var_set_format(mi_h *h, mi_gvar *var, enum mi_gvar_fmt format);
698/* Fill the format field with info from gdb. */
699int gmi_var_show_format(mi_h *h, mi_gvar *var);
700/* Fill the numchild field with info from gdb. */
701int gmi_var_info_num_children(mi_h *h, mi_gvar *var);
702/* Fill the type field with info from gdb. */
703int gmi_var_info_type(mi_h *h, mi_gvar *var);
704/* Fill the expression and lang fields with info from gdb.
705 Note that lang isn't filled during creation. */
706int gmi_var_info_expression(mi_h *h, mi_gvar *var);
707/* Fill the attr field with info from gdb.
708 Note that attr isn't filled during creation. */
709int gmi_var_show_attributes(mi_h *h, mi_gvar *var);
710/* Update variable. Use NULL for all.
711 Note that *changed can be NULL if none updated. */
712int gmi_var_update(mi_h *h, mi_gvar *var, mi_gvar_chg **changed);
713/* Change variable. Fills the value field. */
714int gmi_var_assign(mi_h *h, mi_gvar *var, const char *expression);
715/* Get current value for a variable. */
716int gmi_var_evaluate_expression(mi_h *h, mi_gvar *var);
717/* List children. It ONLY returns the first level information. :-( */
718int gmi_var_list_children(mi_h *h, mi_gvar *var);
719
720
721
722
723#if 0 /* keep Emacsens' auto-indent happy */
724{
725#endif
726#ifdef __cplusplus
727}
728#endif
729
730#endif
diff --git a/pathologist/src/include/monkey_common.h b/pathologist/src/include/monkey_common.h
new file mode 100644
index 0000000..4973ac5
--- /dev/null
+++ b/pathologist/src/include/monkey_common.h
@@ -0,0 +1,443 @@
1/**
2 * @file monkey_common.h
3 * @brief commonly used definitions
4 * @author Safey A.Halim
5 */
6
7#ifndef MONKEY_COMMON_H
8#define MONKEY_COMMON_H
9
10#include <stdio.h>
11/**
12 * Named constants for return values.
13 */
14#define MONKEY_OK 1
15#define MONKEY_SYSERR -1
16#define MONKEY_YES 1
17#define MONKEY_NO 0
18
19/**
20 * Use this for fatal errors that cannot be handled
21 */
22#define MONKEY_assert(cond) do { if (! (cond)) { fprintf(stderr, "Assertion failed at %s:%d.\n", __FILE__, __LINE__); abort(); } } while(0)
23
24/**
25 * Use this for fatal errors that cannot be handled
26 */
27#define MONKEY_assert_at(cond, f, l) do { if (! (cond)) { fprintf(stderr, "Assertion failed at %s:%d.\n", f, l); abort(); } } while(0)
28
29/**
30 * Use this for internal assertion violations that are
31 * not fatal (can be handled) but should not occur.
32 */
33#define MONKEY_break(cond) do { if (! (cond)) { fprintf(stderr, "Assertion failed at %s:%d.\n", __FILE__, __LINE__); } } while(0)
34
35
36/* ******************** doubly-linked list *************** */
37/* To avoid mistakes: head->prev == tail->next == NULL */
38
39/**
40 * Insert an element at the head of a DLL. Assumes that head, tail and
41 * element are structs with prev and next fields.
42 *
43 * @param head pointer to the head of the DLL
44 * @param tail pointer to the tail of the DLL
45 * @param element element to insert
46 */
47#define MONKEY_CONTAINER_DLL_insert(head,tail,element) do { \
48 MONKEY_assert ( ( (element)->prev == NULL) && ((head) != (element))); \
49 MONKEY_assert ( ( (element)->next == NULL) && ((tail) != (element))); \
50 (element)->next = (head); \
51 (element)->prev = NULL; \
52 if ((tail) == NULL) \
53 (tail) = element; \
54 else \
55 (head)->prev = element; \
56 (head) = (element); } while (0)
57
58
59/**
60 * Insert an element at the tail of a DLL. Assumes that head, tail and
61 * element are structs with prev and next fields.
62 *
63 * @param head pointer to the head of the DLL
64 * @param tail pointer to the tail of the DLL
65 * @param element element to insert
66 */
67#define MONKEY_CONTAINER_DLL_insert_tail(head,tail,element) do { \
68 MONKEY_assert ( ( (element)->prev == NULL) && ((head) != (element))); \
69 MONKEY_assert ( ( (element)->next == NULL) && ((tail) != (element))); \
70 (element)->prev = (tail); \
71 (element)->next = NULL; \
72 if ((head) == NULL) \
73 (head) = element; \
74 else \
75 (tail)->next = element; \
76 (tail) = (element); } while (0)
77
78
79/**
80 * Insert an element into a DLL after the given other element. Insert
81 * at the head if the other element is NULL.
82 *
83 * @param head pointer to the head of the DLL
84 * @param tail pointer to the tail of the DLL
85 * @param other prior element, NULL for insertion at head of DLL
86 * @param element element to insert
87 */
88#define MONKEY_CONTAINER_DLL_insert_after(head,tail,other,element) do { \
89 MONKEY_assert ( ( (element)->prev == NULL) && ((head) != (element))); \
90 MONKEY_assert ( ( (element)->next == NULL) && ((tail) != (element))); \
91 (element)->prev = (other); \
92 if (NULL == other) \
93 { \
94 (element)->next = (head); \
95 (head) = (element); \
96 } \
97 else \
98 { \
99 (element)->next = (other)->next; \
100 (other)->next = (element); \
101 } \
102 if (NULL == (element)->next) \
103 (tail) = (element); \
104 else \
105 (element)->next->prev = (element); } while (0)
106
107
108/**
109 * Insert an element into a DLL before the given other element. Insert
110 * at the tail if the other element is NULL.
111 *
112 * @param head pointer to the head of the DLL
113 * @param tail pointer to the tail of the DLL
114 * @param other prior element, NULL for insertion at head of DLL
115 * @param element element to insert
116 */
117#define MONKEY_CONTAINER_DLL_insert_before(head,tail,other,element) do { \
118 MONKEY_assert ( ( (element)->prev == NULL) && ((head) != (element))); \
119 MONKEY_assert ( ( (element)->next == NULL) && ((tail) != (element))); \
120 (element)->next = (other); \
121 if (NULL == other) \
122 { \
123 (element)->prev = (tail); \
124 (tail) = (element); \
125 } \
126 else \
127 { \
128 (element)->prev = (other)->prev; \
129 (other)->prev = (element); \
130 } \
131 if (NULL == (element)->prev) \
132 (head) = (element); \
133 else \
134 (element)->prev->next = (element); } while (0)
135
136
137/**
138 * Remove an element from a DLL. Assumes
139 * that head, tail and element are structs
140 * with prev and next fields.
141 *
142 * @param head pointer to the head of the DLL
143 * @param tail pointer to the tail of the DLL
144 * @param element element to remove
145 */
146#define MONKEY_CONTAINER_DLL_remove(head,tail,element) do { \
147 MONKEY_assert ( ( (element)->prev != NULL) || ((head) == (element))); \
148 MONKEY_assert ( ( (element)->next != NULL) || ((tail) == (element))); \
149 if ((element)->prev == NULL) \
150 (head) = (element)->next; \
151 else \
152 (element)->prev->next = (element)->next; \
153 if ((element)->next == NULL) \
154 (tail) = (element)->prev; \
155 else \
156 (element)->next->prev = (element)->prev; \
157 (element)->next = NULL; \
158 (element)->prev = NULL; } while (0)
159
160
161#define MONKEY_MIN(a,b) (((a) < (b)) ? (a) : (b))
162
163#define MONKEY_MAX(a,b) (((a) > (b)) ? (a) : (b))
164
165/* ************************* allocation functions ****************** */
166
167/**
168 * Maximum allocation with MONKEY_malloc macro.
169 */
170#define MONKEY_MAX_MALLOC_CHECKED (1024 * 1024 * 40)
171
172/**
173 * Wrapper around malloc. Allocates size bytes of memory.
174 * The memory will be zero'ed out.
175 *
176 * @param size the number of bytes to allocate, must be
177 * smaller than 40 MB.
178 * @return pointer to size bytes of memory, never NULL (!)
179 */
180#define MONKEY_malloc(size) MONKEY_xmalloc_(size, __FILE__, __LINE__)
181
182/**
183 * Allocate and initialize a block of memory.
184 *
185 * @param buf data to initalize the block with
186 * @param size the number of bytes in buf (and size of the allocation)
187 * @return pointer to size bytes of memory, never NULL (!)
188 */
189#define MONKEY_memdup(buf,size) MONKEY_xmemdup_(buf, size, __FILE__, __LINE__)
190
191/**
192 * Wrapper around malloc. Allocates size bytes of memory.
193 * The memory will be zero'ed out.
194 *
195 * @param size the number of bytes to allocate
196 * @return pointer to size bytes of memory, NULL if we do not have enough memory
197 */
198#define MONKEY_malloc_large(size) MONKEY_xmalloc_unchecked_(size, __FILE__, __LINE__)
199
200/**
201 * Wrapper around realloc. Rellocates size bytes of memory.
202 *
203 * @param ptr the pointer to reallocate
204 * @param size the number of bytes to reallocate
205 * @return pointer to size bytes of memory
206 */
207#define MONKEY_realloc(ptr, size) MONKEY_xrealloc_(ptr, size, __FILE__, __LINE__)
208
209/**
210 * Wrapper around free. Frees the memory referred to by ptr.
211 * Note that is is generally better to free memory that was
212 * allocated with MONKEY_array_grow using MONKEY_array_grow(mem, size, 0) instead of MONKEY_free.
213 *
214 * @param ptr location where to free the memory. ptr must have
215 * been returned by MONKEY_strdup, MONKEY_strndup, MONKEY_malloc or MONKEY_array_grow earlier.
216 */
217#define MONKEY_free(ptr) MONKEY_xfree_(ptr, __FILE__, __LINE__)
218
219/**
220 * Free the memory pointed to by ptr if ptr is not NULL.
221 * Equivalent to if (ptr!=null)MONKEY_free(ptr).
222 *
223 * @param ptr the location in memory to free
224 */
225#define MONKEY_free_non_null(ptr) do { void * __x__ = ptr; if (__x__ != NULL) { MONKEY_free(__x__); } } while(0)
226
227/**
228 * Wrapper around MONKEY_strdup. Makes a copy of the zero-terminated string
229 * pointed to by a.
230 *
231 * @param a pointer to a zero-terminated string
232 * @return a copy of the string including zero-termination
233 */
234#define MONKEY_strdup(a) MONKEY_xstrdup_(a,__FILE__,__LINE__)
235
236/**
237 * Wrapper around MONKEY_strndup. Makes a partial copy of the string
238 * pointed to by a.
239 *
240 * @param a pointer to a string
241 * @param length of the string to duplicate
242 * @return a partial copy of the string including zero-termination
243 */
244#define MONKEY_strndup(a,length) MONKEY_xstrndup_(a,length,__FILE__,__LINE__)
245
246/**
247 * Grow a well-typed (!) array. This is a convenience
248 * method to grow a vector <tt>arr</tt> of size <tt>size</tt>
249 * to the new (target) size <tt>tsize</tt>.
250 * <p>
251 *
252 * Example (simple, well-typed stack):
253 *
254 * <pre>
255 * static struct foo * myVector = NULL;
256 * static int myVecLen = 0;
257 *
258 * static void push(struct foo * elem) {
259 * MONKEY_array_grow(myVector, myVecLen, myVecLen+1);
260 * memcpy(&myVector[myVecLen-1], elem, sizeof(struct foo));
261 * }
262 *
263 * static void pop(struct foo * elem) {
264 * if (myVecLen == 0) die();
265 * memcpy(elem, myVector[myVecLen-1], sizeof(struct foo));
266 * MONKEY_array_grow(myVector, myVecLen, myVecLen-1);
267 * }
268 * </pre>
269 *
270 * @param arr base-pointer of the vector, may be NULL if size is 0;
271 * will be updated to reflect the new address. The TYPE of
272 * arr is important since size is the number of elements and
273 * not the size in bytes
274 * @param size the number of elements in the existing vector (number
275 * of elements to copy over)
276 * @param tsize the target size for the resulting vector, use 0 to
277 * free the vector (then, arr will be NULL afterwards).
278 */
279#define MONKEY_array_grow(arr,size,tsize) MONKEY_xgrow_((void**)&arr, sizeof(arr[0]), &size, tsize, __FILE__, __LINE__)
280
281/**
282 * Append an element to a list (growing the
283 * list by one).
284 */
285#define MONKEY_array_append(arr,size,element) do { MONKEY_array_grow(arr,size,size+1); arr[size-1] = element; } while(0)
286
287/**
288 * Like snprintf, just aborts if the buffer is of insufficient size.
289 *
290 * @param buf pointer to buffer that is written to
291 * @param size number of bytes in buf
292 * @param format format strings
293 * @param ... data for format string
294 * @return number of bytes written to buf or negative value on error
295 */
296int
297MONKEY_snprintf (char *buf, size_t size, const char *format, ...);
298
299
300/**
301 * Like asprintf, just portable.
302 *
303 * @param buf set to a buffer of sufficient size (allocated, caller must free)
304 * @param format format string (see printf, fprintf, etc.)
305 * @param ... data for format string
306 * @return number of bytes in "*buf" excluding 0-termination
307 */
308int
309MONKEY_asprintf (char **buf, const char *format, ...);
310
311
312/**
313 * Replace all occurrences of a substring in a string
314 * @param string target string
315 * @param substr substring to replace
316 * @param replacement substitution of the substring
317 */
318char *
319MONKEY_str_replace_all ( const char *string, const char *substr, const char *replacement );
320
321/* ************** internal implementations, use macros above! ************** */
322
323/**
324 * Allocate memory. Checks the return value, aborts if no more
325 * memory is available. Don't use MONKEY_xmalloc_ directly. Use the
326 * MONKEY_malloc macro.
327 * The memory will be zero'ed out.
328 *
329 * @param size number of bytes to allocate
330 * @param filename where is this call being made (for debugging)
331 * @param linenumber line where this call is being made (for debugging)
332 * @return allocated memory, never NULL
333 */
334void *
335MONKEY_xmalloc_ (size_t size, const char *filename, int linenumber);
336
337
338
339/**
340 * Allocate and initialize memory. Checks the return value, aborts if no more
341 * memory is available. Don't use MONKEY_xmemdup_ directly. Use the
342 * MONKEY_memdup macro.
343 *
344 * @param buf buffer to initialize from (must contain size bytes)
345 * @param size number of bytes to allocate
346 * @param filename where is this call being made (for debugging)
347 * @param linenumber line where this call is being made (for debugging)
348 * @return allocated memory, never NULL
349 */
350void *
351MONKEY_xmemdup_ (const void *buf, size_t size, const char *filename,
352 int linenumber);
353
354
355/**
356 * Allocate memory. This function does not check if the allocation
357 * request is within reasonable bounds, allowing allocations larger
358 * than 40 MB. If you don't expect the possibility of very large
359 * allocations, use MONKEY_malloc instead. The memory will be zero'ed
360 * out.
361 *
362 * @param size number of bytes to allocate
363 * @param filename where is this call being made (for debugging)
364 * @param linenumber line where this call is being made (for debugging)
365 * @return pointer to size bytes of memory, NULL if we do not have enough memory
366 */
367void *
368MONKEY_xmalloc_unchecked_ (size_t size, const char *filename, int linenumber);
369
370/**
371 * Reallocate memory. Checks the return value, aborts if no more
372 * memory is available.
373 */
374void *
375MONKEY_xrealloc_ (void *ptr, size_t n, const char *filename, int linenumber);
376
377/**
378 * Free memory. Merely a wrapper for the case that we
379 * want to keep track of allocations. Don't use MONKEY_xfree_
380 * directly. Use the MONKEY_free macro.
381 *
382 * @param ptr pointer to memory to free
383 * @param filename where is this call being made (for debugging)
384 * @param linenumber line where this call is being made (for debugging)
385 */
386void
387MONKEY_xfree_ (void *ptr, const char *filename, int linenumber);
388
389
390/**
391 * Dup a string. Don't call MONKEY_xstrdup_ directly. Use the MONKEY_strdup macro.
392 * @param str string to duplicate
393 * @param filename where is this call being made (for debugging)
394 * @param linenumber line where this call is being made (for debugging)
395 * @return the duplicated string
396 */
397char *
398MONKEY_xstrdup_ (const char *str, const char *filename, int linenumber);
399
400/**
401 * Dup partially a string. Don't call MONKEY_xstrndup_ directly. Use the MONKEY_strndup macro.
402 *
403 * @param str string to duplicate
404 * @param len length of the string to duplicate
405 * @param filename where is this call being made (for debugging)
406 * @param linenumber line where this call is being made (for debugging)
407 * @return the duplicated string
408 */
409char *
410MONKEY_xstrndup_ (const char *str, size_t len, const char *filename,
411 int linenumber);
412
413/**
414 * Grow an array, the new elements are zeroed out.
415 * Grows old by (*oldCount-newCount)*elementSize
416 * bytes and sets *oldCount to newCount.
417 *
418 * Don't call MONKEY_xgrow_ directly. Use the MONKEY_array_grow macro.
419 *
420 * @param old address of the pointer to the array
421 * *old may be NULL
422 * @param elementSize the size of the elements of the array
423 * @param oldCount address of the number of elements in the *old array
424 * @param newCount number of elements in the new array, may be 0 (then *old will be NULL afterwards)
425 * @param filename where is this call being made (for debugging)
426 * @param linenumber line where this call is being made (for debugging)
427 */
428void
429MONKEY_xgrow_ (void **old, size_t elementSize, unsigned int *oldCount,
430 unsigned int newCount, const char *filename, int linenumber);
431
432
433
434
435#if __STDC_VERSION__ < 199901L
436#if __GNUC__ >= 2
437#define __func__ __FUNCTION__
438#else
439#define __func__ "<unknown>"
440#endif
441#endif
442
443#endif /* MONKEY_COMMON_H */
diff --git a/pathologist/src/include/monkey_getopt_lib.h b/pathologist/src/include/monkey_getopt_lib.h
new file mode 100644
index 0000000..50d9cc1
--- /dev/null
+++ b/pathologist/src/include/monkey_getopt_lib.h
@@ -0,0 +1,367 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2011 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file include/gnunet_getopt_lib.h
23 * @brief command line parsing and --help formatting
24 *
25 * @author Christian Grothoff
26 */
27
28#ifndef MONKEY_GETOPT_LIB_H
29#define MONKEY_GETOPT_LIB_H
30
31#ifdef __cplusplus
32extern "C"
33{
34#if 0 /* keep Emacsens' auto-indent happy */
35}
36#endif
37#endif
38
39//#include "gnunet_configuration_lib.h"
40
41/**
42 * @brief General context for command line processors.
43 */
44struct MONKEY_GETOPT_CommandLineProcessorContext
45{
46
47 /**
48 * Name of the application
49 */
50 const char *binaryName;
51
52 /**
53 * Name of application with option summary
54 */
55 const char *binaryOptions;
56
57 /**
58 * Array with all command line options.
59 */
60 const struct MONKEY_GETOPT_CommandLineOption *allOptions;
61
62 /**
63 * Original command line
64 */
65 char *const *argv;
66
67 /**
68 * Total number of argv's.
69 */
70 unsigned int argc;
71
72 /**
73 * Current argument.
74 */
75 unsigned int currentArgument;
76
77};
78
79/**
80 * @brief Process a command line option
81 *
82 * @param ctx context for all options
83 * @param scls specific closure (for this processor)
84 * @param option long name of the option (i.e. "config" for --config)
85 * @param value argument, NULL if none was given
86 * @return MONKEY_OK to continue processing other options, MONKEY_SYSERR to abort
87 */
88typedef int (*MONKEY_GETOPT_CommandLineOptionProcessor) (struct
89 MONKEY_GETOPT_CommandLineProcessorContext
90 * ctx, void *scls,
91 const char *option,
92 const char *value);
93
94/**
95 * @brief Definition of a command line option.
96 */
97struct MONKEY_GETOPT_CommandLineOption
98{
99
100 /**
101 * Short name of the option (use '\\0' for none).
102 */
103 const char shortName;
104
105 /**
106 * Long name of the option (may not be NULL)
107 */
108 const char *name;
109
110 /**
111 * Name of the argument for the user in help text
112 */
113 const char *argumentHelp;
114
115 /**
116 * Help text for the option (description)
117 */
118 const char *description;
119
120 /**
121 * Is an argument required? 0: MONKEY_NO (includes optional), 1: MONKEY_YES.
122 */
123 int require_argument;
124
125 /**
126 * Handler for the option.
127 */
128 MONKEY_GETOPT_CommandLineOptionProcessor processor;
129
130 /**
131 * Specific closure to pass to the processor.
132 */
133 void *scls;
134
135};
136
137/**
138 * Macro defining the option to print the command line
139 * help text (-h option).
140 *
141 * @param about string with brief description of the application
142 */
143#define MONKEY_GETOPT_OPTION_HELP(about) \
144 { 'h', "help", (const char *) NULL, gettext_noop("print this help"), 0, &MONKEY_GETOPT_format_help_, (void *) about }
145
146
147/**
148 * Macro defining the option to print the version of
149 * the application (-v option)
150 *
151 * @param version string with the version number
152 */
153#define MONKEY_GETOPT_OPTION_VERSION(version) \
154 { 'v', "version", (const char *) NULL, gettext_noop("print the version number"), 0, &MONKEY_GETOPT_print_version_, (void *) version }
155
156
157/**
158 * Allow user to specify log file name (-l option)
159 *
160 * @param logfn set to the name of the logfile
161 */
162#define MONKEY_GETOPT_OPTION_LOGFILE(logfn) \
163 { 'l', "logfile", "LOGFILE", gettext_noop("configure logging to write logs to LOGFILE"), 1, &MONKEY_GETOPT_set_string, (void *) logfn }
164
165
166/**
167 * Allow user to specify log level (-L option)
168 *
169 * @param loglev set to the log level
170 */
171#define MONKEY_GETOPT_OPTION_LOGLEVEL(loglev) \
172 { 'L', "log", "LOGLEVEL", gettext_noop("configure logging to use LOGLEVEL"), 1, &MONKEY_GETOPT_set_string, (void *) loglev }
173
174
175/**
176 * Get number of verbose (-V) flags
177 *
178 * @param level where to store the verbosity level (should be an 'int')
179 */
180#define MONKEY_GETOPT_OPTION_VERBOSE(level) \
181 { 'V', "verbose", (const char *) NULL, gettext_noop("be verbose"), 0, &MONKEY_GETOPT_increment_value, (void *) level }
182
183
184/**
185 * Get configuration file name (-c option)
186 *
187 * @param fn set to the configuration file name
188 */
189#define MONKEY_GETOPT_OPTION_CFG_FILE(fn) \
190 { 'c', "config", "FILENAME", gettext_noop("use configuration file FILENAME"), 1, &MONKEY_GETOPT_set_string, (void *) fn }
191
192
193/**
194 * Marker for the end of the list of options.
195 */
196#define MONKEY_GETOPT_OPTION_END \
197 { '\0', NULL, NULL, NULL, 0, NULL, NULL }
198
199
200/**
201 * Parse the command line.
202 *
203 * @param binaryOptions Name of application with option summary
204 * @param allOptions defined options and handlers
205 * @param argc number of arguments
206 * @param argv actual arguments
207 * @return index into argv with first non-option
208 * argument, or MONKEY_SYSERR on error
209 */
210int
211MONKEY_GETOPT_run (const char *binaryOptions,
212 const struct MONKEY_GETOPT_CommandLineOption *allOptions,
213 unsigned int argc, char *const *argv);
214
215
216/**
217 * Set an option of type 'unsigned long long' from the command line.
218 * A pointer to this function should be passed as part of the
219 * 'struct MONKEY_GETOPT_CommandLineOption' array to initialize options
220 * of this type. It should be followed by a pointer to a value of
221 * type 'unsigned long long'.
222 *
223 * @param ctx command line processing context
224 * @param scls additional closure (will point to the 'unsigned long long')
225 * @param option name of the option
226 * @param value actual value of the option as a string.
227 * @return MONKEY_OK if parsing the value worked
228 */
229int
230MONKEY_GETOPT_set_ulong (struct MONKEY_GETOPT_CommandLineProcessorContext *ctx,
231 void *scls, const char *option, const char *value);
232
233
234/**
235 * Set an option of type 'struct MONKEY_TIME_Relative' from the command line.
236 * A pointer to this function should be passed as part of the
237 * 'struct MONKEY_GETOPT_CommandLineOption' array to initialize options
238 * of this type. It should be followed by a pointer to a value of
239 * type 'struct MONKEY_TIME_Relative'.
240 *
241 * @param ctx command line processing context
242 * @param scls additional closure (will point to the 'struct MONKEY_TIME_Relative')
243 * @param option name of the option
244 * @param value actual value of the option as a string.
245 * @return MONKEY_OK if parsing the value worked
246 */
247int
248MONKEY_GETOPT_set_relative_time (struct MONKEY_GETOPT_CommandLineProcessorContext *ctx,
249 void *scls, const char *option, const char *value);
250
251
252/**
253 * Set an option of type 'unsigned int' from the command line.
254 * A pointer to this function should be passed as part of the
255 * 'struct MONKEY_GETOPT_CommandLineOption' array to initialize options
256 * of this type. It should be followed by a pointer to a value of
257 * type 'unsigned int'.
258 *
259 * @param ctx command line processing context
260 * @param scls additional closure (will point to the 'unsigned int')
261 * @param option name of the option
262 * @param value actual value of the option as a string.
263 * @return MONKEY_OK if parsing the value worked
264 */
265int
266MONKEY_GETOPT_set_uint (struct MONKEY_GETOPT_CommandLineProcessorContext *ctx,
267 void *scls, const char *option, const char *value);
268
269
270/**
271 * Set an option of type 'int' from the command line to 1 if the
272 * given option is present.
273 * A pointer to this function should be passed as part of the
274 * 'struct MONKEY_GETOPT_CommandLineOption' array to initialize options
275 * of this type. It should be followed by a pointer to a value of
276 * type 'int'.
277 *
278 * @param ctx command line processing context
279 * @param scls additional closure (will point to the 'int')
280 * @param option name of the option
281 * @param value not used (NULL)
282 * @return MONKEY_OK
283 */
284int
285MONKEY_GETOPT_set_one (struct MONKEY_GETOPT_CommandLineProcessorContext *ctx,
286 void *scls, const char *option, const char *value);
287
288
289/**
290 * Set an option of type 'char *' from the command line.
291 * A pointer to this function should be passed as part of the
292 * 'struct MONKEY_GETOPT_CommandLineOption' array to initialize options
293 * of this type. It should be followed by a pointer to a value of
294 * type 'char *'.
295 *
296 * @param ctx command line processing context
297 * @param scls additional closure (will point to the 'char *',
298 * which will be allocated)
299 * @param option name of the option
300 * @param value actual value of the option (a string)
301 * @return MONKEY_OK
302 */
303int
304MONKEY_GETOPT_set_string (struct MONKEY_GETOPT_CommandLineProcessorContext *ctx,
305 void *scls, const char *option, const char *value);
306
307/**
308 * Set an option of type 'unsigned int' from the command line. Each
309 * time the option flag is given, the value is incremented by one.
310 * A pointer to this function should be passed as part of the
311 * 'struct MONKEY_GETOPT_CommandLineOption' array to initialize options
312 * of this type. It should be followed by a pointer to a value of
313 * type 'int'.
314 *
315 * @param ctx command line processing context
316 * @param scls additional closure (will point to the 'int')
317 * @param option name of the option
318 * @param value not used (NULL)
319 * @return MONKEY_OK
320 */
321int
322MONKEY_GETOPT_increment_value (struct MONKEY_GETOPT_CommandLineProcessorContext
323 *ctx, void *scls, const char *option,
324 const char *value);
325
326
327/* *************** internal prototypes - use macros above! ************* */
328
329/**
330 * Print out details on command line options (implements --help).
331 *
332 * @param ctx command line processing context
333 * @param scls additional closure (points to about text)
334 * @param option name of the option
335 * @param value not used (NULL)
336 * @return MONKEY_SYSERR (do not continue)
337 */
338int
339MONKEY_GETOPT_format_help_ (struct MONKEY_GETOPT_CommandLineProcessorContext
340 *ctx, void *scls, const char *option,
341 const char *value);
342
343/**
344 * Print out program version (implements --version).
345 *
346 * @param ctx command line processing context
347 * @param scls additional closure (points to version string)
348 * @param option name of the option
349 * @param value not used (NULL)
350 * @return MONKEY_SYSERR (do not continue)
351 */
352int
353MONKEY_GETOPT_print_version_ (struct MONKEY_GETOPT_CommandLineProcessorContext
354 *ctx, void *scls, const char *option,
355 const char *value);
356
357#if 0 /* keep Emacsens' auto-indent happy */
358{
359#endif
360#ifdef __cplusplus
361}
362#endif
363
364
365/* ifndef MONKEY_GETOPT_LIB_H */
366#endif
367/* end of gnunet_getopt_lib.h */
diff --git a/pathologist/src/include/mxml.h b/pathologist/src/include/mxml.h
new file mode 100644
index 0000000..79c711f
--- /dev/null
+++ b/pathologist/src/include/mxml.h
@@ -0,0 +1,329 @@
1/*
2 * "$Id: mxml.h 427 2011-01-03 02:03:29Z mike $"
3 *
4 * Header file for Mini-XML, a small XML-like file parsing library.
5 *
6 * Copyright 2003-2011 by Michael R Sweet.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Michael R Sweet and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file. If this file is
12 * missing or damaged, see the license at:
13 *
14 * http://www.minixml.org/
15 */
16
17/*
18 * Prevent multiple inclusion...
19 */
20
21#ifndef _mxml_h_
22# define _mxml_h_
23
24/*
25 * Include necessary headers...
26 */
27
28# include <stdio.h>
29# include <stdlib.h>
30# include <string.h>
31# include <ctype.h>
32# include <errno.h>
33
34
35/*
36 * Constants...
37 */
38
39# define MXML_TAB 8 /* Tabs every N columns */
40
41# define MXML_NO_CALLBACK 0 /* Don't use a type callback */
42# define MXML_INTEGER_CALLBACK mxml_integer_cb
43 /* Treat all data as integers */
44# define MXML_OPAQUE_CALLBACK mxml_opaque_cb
45 /* Treat all data as opaque */
46# define MXML_REAL_CALLBACK mxml_real_cb
47 /* Treat all data as real numbers */
48# define MXML_TEXT_CALLBACK 0 /* Treat all data as text */
49# define MXML_IGNORE_CALLBACK mxml_ignore_cb
50 /* Ignore all non-element content */
51
52# define MXML_NO_PARENT 0 /* No parent for the node */
53
54# define MXML_DESCEND 1 /* Descend when finding/walking */
55# define MXML_NO_DESCEND 0 /* Don't descend when finding/walking */
56# define MXML_DESCEND_FIRST -1 /* Descend for first find */
57
58# define MXML_WS_BEFORE_OPEN 0 /* Callback for before open tag */
59# define MXML_WS_AFTER_OPEN 1 /* Callback for after open tag */
60# define MXML_WS_BEFORE_CLOSE 2 /* Callback for before close tag */
61# define MXML_WS_AFTER_CLOSE 3 /* Callback for after close tag */
62
63# define MXML_ADD_BEFORE 0 /* Add node before specified node */
64# define MXML_ADD_AFTER 1 /* Add node after specified node */
65# define MXML_ADD_TO_PARENT NULL /* Add node relative to parent */
66
67
68/*
69 * Data types...
70 */
71
72typedef enum mxml_sax_event_e /**** SAX event type. ****/
73{
74 MXML_SAX_CDATA, /* CDATA node */
75 MXML_SAX_COMMENT, /* Comment node */
76 MXML_SAX_DATA, /* Data node */
77 MXML_SAX_DIRECTIVE, /* Processing directive node */
78 MXML_SAX_ELEMENT_CLOSE, /* Element closed */
79 MXML_SAX_ELEMENT_OPEN /* Element opened */
80} mxml_sax_event_t;
81
82typedef enum mxml_type_e /**** The XML node type. ****/
83{
84 MXML_IGNORE = -1, /* Ignore/throw away node @since Mini-XML 2.3@ */
85 MXML_ELEMENT, /* XML element with attributes */
86 MXML_INTEGER, /* Integer value */
87 MXML_OPAQUE, /* Opaque string */
88 MXML_REAL, /* Real value */
89 MXML_TEXT, /* Text fragment */
90 MXML_CUSTOM /* Custom data @since Mini-XML 2.1@ */
91} mxml_type_t;
92
93typedef void (*mxml_custom_destroy_cb_t)(void *);
94 /**** Custom data destructor ****/
95
96typedef void (*mxml_error_cb_t)(const char *);
97 /**** Error callback function ****/
98
99typedef struct mxml_attr_s /**** An XML element attribute value. @private@ ****/
100{
101 char *name; /* Attribute name */
102 char *value; /* Attribute value */
103} mxml_attr_t;
104
105typedef struct mxml_element_s /**** An XML element value. @private@ ****/
106{
107 char *name; /* Name of element */
108 int num_attrs; /* Number of attributes */
109 mxml_attr_t *attrs; /* Attributes */
110} mxml_element_t;
111
112typedef struct mxml_text_s /**** An XML text value. @private@ ****/
113{
114 int whitespace; /* Leading whitespace? */
115 char *string; /* Fragment string */
116} mxml_text_t;
117
118typedef struct mxml_custom_s /**** An XML custom value. @private@ ****/
119{
120 void *data; /* Pointer to (allocated) custom data */
121 mxml_custom_destroy_cb_t destroy; /* Pointer to destructor function */
122} mxml_custom_t;
123
124typedef union mxml_value_u /**** An XML node value. @private@ ****/
125{
126 mxml_element_t element; /* Element */
127 int integer; /* Integer number */
128 char *opaque; /* Opaque string */
129 double real; /* Real number */
130 mxml_text_t text; /* Text fragment */
131 mxml_custom_t custom; /* Custom data @since Mini-XML 2.1@ */
132} mxml_value_t;
133
134struct mxml_node_s /**** An XML node. @private@ ****/
135{
136 mxml_type_t type; /* Node type */
137 struct mxml_node_s *next; /* Next node under same parent */
138 struct mxml_node_s *prev; /* Previous node under same parent */
139 struct mxml_node_s *parent; /* Parent node */
140 struct mxml_node_s *child; /* First child node */
141 struct mxml_node_s *last_child; /* Last child node */
142 mxml_value_t value; /* Node value */
143 int ref_count; /* Use count */
144 void *user_data; /* User data */
145};
146
147typedef struct mxml_node_s mxml_node_t; /**** An XML node. ****/
148
149struct mxml_index_s /**** An XML node index. @private@ ****/
150{
151 char *attr; /* Attribute used for indexing or NULL */
152 int num_nodes; /* Number of nodes in index */
153 int alloc_nodes; /* Allocated nodes in index */
154 int cur_node; /* Current node */
155 mxml_node_t **nodes; /* Node array */
156};
157
158typedef struct mxml_index_s mxml_index_t;
159 /**** An XML node index. ****/
160
161typedef int (*mxml_custom_load_cb_t)(mxml_node_t *, const char *);
162 /**** Custom data load callback function ****/
163
164typedef char *(*mxml_custom_save_cb_t)(mxml_node_t *);
165 /**** Custom data save callback function ****/
166
167typedef int (*mxml_entity_cb_t)(const char *);
168 /**** Entity callback function */
169
170typedef mxml_type_t (*mxml_load_cb_t)(mxml_node_t *);
171 /**** Load callback function ****/
172
173typedef const char *(*mxml_save_cb_t)(mxml_node_t *, int);
174 /**** Save callback function ****/
175
176typedef void (*mxml_sax_cb_t)(mxml_node_t *, mxml_sax_event_t, void *);
177 /**** SAX callback function ****/
178
179
180/*
181 * C++ support...
182 */
183
184# ifdef __cplusplus
185extern "C" {
186# endif /* __cplusplus */
187
188/*
189 * Prototypes...
190 */
191
192extern void mxmlAdd(mxml_node_t *parent, int where,
193 mxml_node_t *child, mxml_node_t *node);
194extern void mxmlDelete(mxml_node_t *node);
195extern void mxmlElementDeleteAttr(mxml_node_t *node,
196 const char *name);
197extern const char *mxmlElementGetAttr(mxml_node_t *node, const char *name);
198extern void mxmlElementSetAttr(mxml_node_t *node, const char *name,
199 const char *value);
200extern void mxmlElementSetAttrf(mxml_node_t *node, const char *name,
201 const char *format, ...)
202# ifdef __GNUC__
203__attribute__ ((__format__ (__printf__, 3, 4)))
204# endif /* __GNUC__ */
205;
206extern int mxmlEntityAddCallback(mxml_entity_cb_t cb);
207extern const char *mxmlEntityGetName(int val);
208extern int mxmlEntityGetValue(const char *name);
209extern void mxmlEntityRemoveCallback(mxml_entity_cb_t cb);
210extern mxml_node_t *mxmlFindElement(mxml_node_t *node, mxml_node_t *top,
211 const char *name, const char *attr,
212 const char *value, int descend);
213extern mxml_node_t *mxmlFindPath(mxml_node_t *node, const char *path);
214extern const char *mxmlGetCDATA(mxml_node_t *node);
215extern const void *mxmlGetCustom(mxml_node_t *node);
216extern const char *mxmlGetElement(mxml_node_t *node);
217extern mxml_node_t *mxmlGetFirstChild(mxml_node_t *node);
218extern int mxmlGetInteger(mxml_node_t *node);
219extern mxml_node_t *mxmlGetLastChild(mxml_node_t *node);
220extern mxml_node_t *mxmlGetNextSibling(mxml_node_t *node);
221extern const char *mxmlGetOpaque(mxml_node_t *node);
222extern mxml_node_t *mxmlGetParent(mxml_node_t *node);
223extern mxml_node_t *mxmlGetPrevSibling(mxml_node_t *node);
224extern double mxmlGetReal(mxml_node_t *node);
225extern int mxmlGetRefCount(mxml_node_t *node);
226extern const char *mxmlGetText(mxml_node_t *node, int *whitespace);
227extern mxml_type_t mxmlGetType(mxml_node_t *node);
228extern void *mxmlGetUserData(mxml_node_t *node);
229extern void mxmlIndexDelete(mxml_index_t *ind);
230extern mxml_node_t *mxmlIndexEnum(mxml_index_t *ind);
231extern mxml_node_t *mxmlIndexFind(mxml_index_t *ind,
232 const char *element,
233 const char *value);
234extern int mxmlIndexGetCount(mxml_index_t *ind);
235extern mxml_index_t *mxmlIndexNew(mxml_node_t *node, const char *element,
236 const char *attr);
237extern mxml_node_t *mxmlIndexReset(mxml_index_t *ind);
238extern mxml_node_t *mxmlLoadFd(mxml_node_t *top, int fd,
239 mxml_type_t (*cb)(mxml_node_t *));
240extern mxml_node_t *mxmlLoadFile(mxml_node_t *top, FILE *fp,
241 mxml_type_t (*cb)(mxml_node_t *));
242extern mxml_node_t *mxmlLoadString(mxml_node_t *top, const char *s,
243 mxml_type_t (*cb)(mxml_node_t *));
244extern mxml_node_t *mxmlNewCDATA(mxml_node_t *parent, const char *string);
245extern mxml_node_t *mxmlNewCustom(mxml_node_t *parent, void *data,
246 mxml_custom_destroy_cb_t destroy);
247extern mxml_node_t *mxmlNewElement(mxml_node_t *parent, const char *name);
248extern mxml_node_t *mxmlNewInteger(mxml_node_t *parent, int integer);
249extern mxml_node_t *mxmlNewOpaque(mxml_node_t *parent, const char *opaque);
250extern mxml_node_t *mxmlNewReal(mxml_node_t *parent, double real);
251extern mxml_node_t *mxmlNewText(mxml_node_t *parent, int whitespace,
252 const char *string);
253extern mxml_node_t *mxmlNewTextf(mxml_node_t *parent, int whitespace,
254 const char *format, ...)
255# ifdef __GNUC__
256__attribute__ ((__format__ (__printf__, 3, 4)))
257# endif /* __GNUC__ */
258;
259extern mxml_node_t *mxmlNewXML(const char *version);
260extern int mxmlRelease(mxml_node_t *node);
261extern void mxmlRemove(mxml_node_t *node);
262extern int mxmlRetain(mxml_node_t *node);
263extern char *mxmlSaveAllocString(mxml_node_t *node,
264 mxml_save_cb_t cb);
265extern int mxmlSaveFd(mxml_node_t *node, int fd,
266 mxml_save_cb_t cb);
267extern int mxmlSaveFile(mxml_node_t *node, FILE *fp,
268 mxml_save_cb_t cb);
269extern int mxmlSaveString(mxml_node_t *node, char *buffer,
270 int bufsize, mxml_save_cb_t cb);
271extern mxml_node_t *mxmlSAXLoadFd(mxml_node_t *top, int fd,
272 mxml_type_t (*cb)(mxml_node_t *),
273 mxml_sax_cb_t sax, void *sax_data);
274extern mxml_node_t *mxmlSAXLoadFile(mxml_node_t *top, FILE *fp,
275 mxml_type_t (*cb)(mxml_node_t *),
276 mxml_sax_cb_t sax, void *sax_data);
277extern mxml_node_t *mxmlSAXLoadString(mxml_node_t *top, const char *s,
278 mxml_type_t (*cb)(mxml_node_t *),
279 mxml_sax_cb_t sax, void *sax_data);
280extern int mxmlSetCDATA(mxml_node_t *node, const char *data);
281extern int mxmlSetCustom(mxml_node_t *node, void *data,
282 mxml_custom_destroy_cb_t destroy);
283extern void mxmlSetCustomHandlers(mxml_custom_load_cb_t load,
284 mxml_custom_save_cb_t save);
285extern int mxmlSetElement(mxml_node_t *node, const char *name);
286extern void mxmlSetErrorCallback(mxml_error_cb_t cb);
287extern int mxmlSetInteger(mxml_node_t *node, int integer);
288extern int mxmlSetOpaque(mxml_node_t *node, const char *opaque);
289extern int mxmlSetReal(mxml_node_t *node, double real);
290extern int mxmlSetText(mxml_node_t *node, int whitespace,
291 const char *string);
292extern int mxmlSetTextf(mxml_node_t *node, int whitespace,
293 const char *format, ...)
294# ifdef __GNUC__
295__attribute__ ((__format__ (__printf__, 3, 4)))
296# endif /* __GNUC__ */
297;
298extern int mxmlSetUserData(mxml_node_t *node, void *data);
299extern void mxmlSetWrapMargin(int column);
300extern mxml_node_t *mxmlWalkNext(mxml_node_t *node, mxml_node_t *top,
301 int descend);
302extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top,
303 int descend);
304
305
306/*
307 * Semi-private functions...
308 */
309
310extern void mxml_error(const char *format, ...);
311extern mxml_type_t mxml_ignore_cb(mxml_node_t *node);
312extern mxml_type_t mxml_integer_cb(mxml_node_t *node);
313extern mxml_type_t mxml_opaque_cb(mxml_node_t *node);
314extern mxml_type_t mxml_real_cb(mxml_node_t *node);
315
316
317/*
318 * C++ support...
319 */
320
321# ifdef __cplusplus
322}
323# endif /* __cplusplus */
324#endif /* !_mxml_h_ */
325
326
327/*
328 * End of "$Id: mxml.h 427 2011-01-03 02:03:29Z mike $".
329 */
diff --git a/pathologist/src/include/platform.h b/pathologist/src/include/platform.h
new file mode 100644
index 0000000..994a5b6
--- /dev/null
+++ b/pathologist/src/include/platform.h
@@ -0,0 +1,266 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file include/platform.h
23 * @brief plaform specifics
24 *
25 * @author Nils Durner
26 *
27 * This file should never be included by installed
28 * header files (thos starting with "gnunet_").
29 */
30
31#ifndef PLATFORM_H
32#define PLATFORM_H
33
34#ifndef HAVE_USED_CONFIG_H
35#define HAVE_USED_CONFIG_H
36#if HAVE_CONFIG_H
37//#include "gnunet_config.h"
38#endif
39#endif
40
41#ifdef WINDOWS
42#define BREAKPOINT asm("int $3;");
43#define GNUNET_SIGCHLD 17
44#else
45#define BREAKPOINT
46#define GNUNET_SIGCHLD SIGCHLD
47#endif
48
49#ifdef HAVE_SYS_TYPES_H
50#include <sys/types.h>
51#endif
52
53#define ALLOW_EXTRA_CHECKS GNUNET_NO
54
55/**
56 * For strptime (glibc2 needs this).
57 */
58#ifndef _XOPEN_SOURCE
59#define _XOPEN_SOURCE
60#endif
61
62#ifndef _REENTRANT
63#define _REENTRANT
64#endif
65
66/* configuration options */
67
68#define VERBOSE_STATS 0
69
70#ifdef CYGWIN
71#include <sys/reent.h>
72#endif
73
74#ifdef _MSC_VER
75#ifndef FD_SETSIZE
76#define FD_SETSIZE 1024
77#endif
78#include <Winsock2.h>
79#include <ws2tcpip.h>
80#else
81#ifndef MINGW
82#include <netdb.h>
83#include <sys/socket.h>
84#include <sys/un.h>
85#if HAVE_NETINET_IN_H
86#include <netinet/in.h>
87#endif
88#if HAVE_NETINET_IN_SYSTM_H
89#include <netinet/in_systm.h>
90#endif
91#include <netinet/ip.h> /* superset of previous */
92#include <arpa/inet.h>
93#include <netinet/tcp.h>
94#include <pwd.h>
95#include <sys/ioctl.h>
96#include <sys/wait.h>
97#include <grp.h>
98#else
99#include "winproc.h"
100#endif
101#endif
102
103#include <string.h>
104#include <stdio.h>
105#include <stdlib.h>
106#include <stdint.h>
107#include <stdarg.h>
108#include <errno.h>
109#include <signal.h>
110#include <libgen.h>
111#ifdef WINDOWS
112#include <malloc.h> /* for alloca(), on other OSes it's in stdlib.h */
113#endif
114#ifndef _MSC_VER
115#include <unistd.h> /* KLB_FIX */
116#endif
117#include <sys/stat.h>
118#include <sys/types.h>
119#ifndef _MSC_VER
120#include <dirent.h> /* KLB_FIX */
121#endif
122#include <fcntl.h>
123#include <math.h>
124#if HAVE_SYS_PARAM_H
125#include <sys/param.h>
126#endif
127#if TIME_WITH_SYS_TIME
128#include <sys/time.h>
129#include <time.h>
130#else
131#if HAVE_SYS_TIME_H
132#include <sys/time.h>
133#else
134#include <time.h>
135#endif
136#endif
137
138#ifdef SOMEBSD
139#include <net/if.h>
140#endif
141#ifdef GNUNET_freeBSD
142#include <semaphore.h>
143#endif
144#ifdef DARWIN
145#include <dlfcn.h>
146#include <semaphore.h>
147#include <net/if.h>
148#endif
149#if defined(LINUX) || defined(GNU)
150#include <net/if.h>
151#endif
152#ifdef SOLARIS
153#include <sys/sockio.h>
154#include <sys/filio.h>
155#include <sys/loadavg.h>
156#include <semaphore.h>
157#endif
158#if HAVE_UCRED_H
159#include <ucred.h>
160#endif
161#ifdef CYGWIN
162#include <windows.h>
163#include <cygwin/if.h>
164#endif
165#if HAVE_IFADDRS_H
166#include <ifaddrs.h>
167#endif
168#include <errno.h>
169#include <limits.h>
170
171#if HAVE_VFORK_H
172#include <vfork.h>
173#endif
174
175#include <ctype.h>
176#if HAVE_SYS_RESOURCE_H
177#include <sys/resource.h>
178#endif
179
180#if HAVE_ENDIAN_H
181#include <endian.h>
182#endif
183#if HAVE_SYS_ENDIAN_H
184#include <sys/endian.h>
185#endif
186
187#include "plibc.h"
188
189//#include <locale.h>
190//#ifndef FRAMEWORK_BUILD
191//#include "gettext.h"
192///**
193// * GNU gettext support macro.
194// */
195//#define _(String) dgettext("gnunet",String)
196//#define LIBEXTRACTOR_GETTEXT_DOMAIN "libextractor"
197//#else
198//#include "libintlemu.h"
199//#define _(String) dgettext("org.gnunet.gnunet",String)
200//#define LIBEXTRACTOR_GETTEXT_DOMAIN "org.gnunet.libextractor"
201//#endif
202
203#ifdef CYGWIN
204#define SIOCGIFCONF _IOW('s', 100, struct ifconf) /* get if list */
205#define SIOCGIFFLAGS _IOW('s', 101, struct ifreq) /* Get if flags */
206#define SIOCGIFADDR _IOW('s', 102, struct ifreq) /* Get if addr */
207#endif
208
209#ifndef MINGW
210#include <sys/mman.h>
211#endif
212
213#ifdef FREEBSD
214#define __BYTE_ORDER BYTE_ORDER
215#define __BIG_ENDIAN BIG_ENDIAN
216#endif
217
218#ifdef DARWIN
219#define __BYTE_ORDER BYTE_ORDER
220#define __BIG_ENDIAN BIG_ENDIAN
221 /* not available on darwin, override configure */
222#undef HAVE_STAT64
223#undef HAVE_MREMAP
224#endif
225
226
227#if !HAVE_ATOLL
228long long
229atoll (const char *nptr);
230#endif
231
232#if ENABLE_NLS
233#include "langinfo.h"
234#endif
235
236#ifndef SIZE_MAX
237#define SIZE_MAX ((size_t)(-1))
238#endif
239
240#ifndef O_LARGEFILE
241#define O_LARGEFILE 0
242#endif
243
244/**
245 * AI_NUMERICSERV not defined in windows. Then we just do without.
246 */
247#ifndef AI_NUMERICSERV
248#define AI_NUMERICSERV 0
249#endif
250
251
252#if defined(__sparc__)
253#define MAKE_UNALIGNED(val) ({ __typeof__((val)) __tmp; memmove(&__tmp, &(val), sizeof((val))); __tmp; })
254#else
255#define MAKE_UNALIGNED(val) val
256#endif
257
258#if WINDOWS
259#define FDTYPE HANDLE
260#define SOCKTYPE SOCKET
261#else
262#define FDTYPE int
263#define SOCKTYPE int
264#endif
265
266#endif
diff --git a/pathologist/src/include/plibc.h b/pathologist/src/include/plibc.h
new file mode 100644
index 0000000..8314f21
--- /dev/null
+++ b/pathologist/src/include/plibc.h
@@ -0,0 +1,810 @@
1/*
2 This file is part of PlibC.
3 (C) 2005, 2006, 2007, 2008, 2009, 2010 Nils Durner (and other contributing authors)
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
19
20/**
21 * @file include/plibc.h
22 * @brief PlibC header
23 * @attention This file is usually not installed under Unix,
24 * so ship it with your application
25 * @version $Revision: 69 $
26 */
27
28#ifndef _PLIBC_H_
29#define _PLIBC_H_
30
31#ifndef SIGALRM
32#define SIGALRM 14
33#endif
34
35#ifdef __cplusplus
36extern "C"
37{
38#endif
39
40#include <stddef.h>
41
42#ifdef Q_OS_WIN32
43#define WINDOWS 1
44#endif
45
46#define HAVE_PLIBC_FD 0
47
48#ifdef WINDOWS
49
50#if ENABLE_NLS
51#include "langinfo.h"
52#endif
53
54#include <winsock2.h>
55#include <ws2tcpip.h>
56#include <windows.h>
57#include <sys/types.h>
58#include <time.h>
59#include <stdio.h>
60#include <sys/types.h>
61#include <sys/stat.h>
62#include <dirent.h>
63#include <errno.h>
64#include <stdarg.h>
65
66#define __BYTE_ORDER BYTE_ORDER
67#define __BIG_ENDIAN BIG_ENDIAN
68
69/* Conflicts with our definitions */
70#define __G_WIN32_H__
71
72/* Convert LARGE_INTEGER to double */
73#define Li2Double(x) ((double)((x).HighPart) * 4.294967296E9 + \
74 (double)((x).LowPart))
75
76 struct stat64
77 {
78 _dev_t st_dev;
79 _ino_t st_ino;
80 _mode_t st_mode;
81 short st_nlink;
82 short st_uid;
83 short st_gid;
84 _dev_t st_rdev;
85 __int64 st_size;
86 __time64_t st_atime;
87 __time64_t st_mtime;
88 __time64_t st_ctime;
89 };
90
91 typedef unsigned int sa_family_t;
92
93 struct sockaddr_un
94 {
95 short sun_family; /*AF_UNIX */
96 char sun_path[108]; /*path name */
97 };
98
99#ifndef pid_t
100#define pid_t DWORD
101#endif
102
103#ifndef error_t
104#define error_t int
105#endif
106
107#ifndef WEXITSTATUS
108#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
109#endif
110
111#ifndef MSG_DONTWAIT
112#define MSG_DONTWAIT 0
113#endif
114
115 enum
116 {
117 _SC_PAGESIZE = 30,
118 _SC_PAGE_SIZE = 30
119 };
120
121/* Thanks to the Cygwin project */
122#define ENOCSI 43 /* No CSI structure available */
123#define EL2HLT 44 /* Level 2 halted */
124#ifndef EDEADLK
125#define EDEADLK 45 /* Deadlock condition */
126#endif
127#ifndef ENOLCK
128#define ENOLCK 46 /* No record locks available */
129#endif
130#define EBADE 50 /* Invalid exchange */
131#define EBADR 51 /* Invalid request descriptor */
132#define EXFULL 52 /* Exchange full */
133#define ENOANO 53 /* No anode */
134#define EBADRQC 54 /* Invalid request code */
135#define EBADSLT 55 /* Invalid slot */
136#ifndef EDEADLOCK
137#define EDEADLOCK EDEADLK /* File locking deadlock error */
138#endif
139#define EBFONT 57 /* Bad font file fmt */
140#define ENOSTR 60 /* Device not a stream */
141#define ENODATA 61 /* No data (for no delay io) */
142#define ETIME 62 /* Timer expired */
143#define ENOSR 63 /* Out of streams resources */
144#define ENONET 64 /* Machine is not on the network */
145#define ENOPKG 65 /* Package not installed */
146#define EREMOTE 66 /* The object is remote */
147#define ENOLINK 67 /* The link has been severed */
148#define EADV 68 /* Advertise error */
149#define ESRMNT 69 /* Srmount error */
150#define ECOMM 70 /* Communication error on send */
151#define EPROTO 71 /* Protocol error */
152#define EMULTIHOP 74 /* Multihop attempted */
153#define ELBIN 75 /* Inode is remote (not really error) */
154#define EDOTDOT 76 /* Cross mount point (not really error) */
155#define EBADMSG 77 /* Trying to read unreadable message */
156#define ENOTUNIQ 80 /* Given log. name not unique */
157#define EBADFD 81 /* f.d. invalid for this operation */
158#define EREMCHG 82 /* Remote address changed */
159#define ELIBACC 83 /* Can't access a needed shared lib */
160#define ELIBBAD 84 /* Accessing a corrupted shared lib */
161#define ELIBSCN 85 /* .lib section in a.out corrupted */
162#define ELIBMAX 86 /* Attempting to link in too many libs */
163#define ELIBEXEC 87 /* Attempting to exec a shared library */
164#ifndef ENOSYS
165#define ENOSYS 88 /* Function not implemented */
166#endif
167#define ENMFILE 89 /* No more files */
168#ifndef ENOTEMPTY
169#define ENOTEMPTY 90 /* Directory not empty */
170#endif
171#ifndef ENAMETOOLONG
172#define ENAMETOOLONG 91 /* File or path name too long */
173#endif
174#define ELOOP 92 /* Too many symbolic links */
175#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
176#define EPFNOSUPPORT 96 /* Protocol family not supported */
177#define ECONNRESET 104 /* Connection reset by peer */
178#define ENOBUFS 105 /* No buffer space available */
179#define EAFNOSUPPORT 106 /* Address family not supported by protocol family */
180#define EPROTOTYPE 107 /* Protocol wrong type for socket */
181#define ENOTSOCK 108 /* Socket operation on non-socket */
182#define ENOPROTOOPT 109 /* Protocol not available */
183#define ESHUTDOWN 110 /* Can't send after socket shutdown */
184#define ECONNREFUSED 111 /* Connection refused */
185#define EADDRINUSE 112 /* Address already in use */
186#define ECONNABORTED 113 /* Connection aborted */
187#define ENETUNREACH 114 /* Network is unreachable */
188#define ENETDOWN 115 /* Network interface is not configured */
189#ifndef ETIMEDOUT
190#define ETIMEDOUT 116 /* Connection timed out */
191#endif
192#define EHOSTDOWN 117 /* Host is down */
193#define EHOSTUNREACH 118 /* Host is unreachable */
194#define EINPROGRESS 119 /* Connection already in progress */
195#define EALREADY 120 /* Socket already connected */
196#define EDESTADDRREQ 121 /* Destination address required */
197#define EMSGSIZE 122 /* Message too long */
198#define EPROTONOSUPPORT 123 /* Unknown protocol */
199#define ESOCKTNOSUPPORT 124 /* Socket type not supported */
200#define EADDRNOTAVAIL 125 /* Address not available */
201#define ENETRESET 126 /* Connection aborted by network */
202#define EISCONN 127 /* Socket is already connected */
203#define ENOTCONN 128 /* Socket is not connected */
204#define ETOOMANYREFS 129 /* Too many references: cannot splice */
205#define EPROCLIM 130 /* Too many processes */
206#define EUSERS 131 /* Too many users */
207#define EDQUOT 132 /* Disk quota exceeded */
208#define ESTALE 133 /* Unknown error */
209#ifndef ENOTSUP
210#define ENOTSUP 134 /* Not supported */
211#endif
212#define ENOMEDIUM 135 /* No medium (in tape drive) */
213#define ENOSHARE 136 /* No such host or network path */
214#define ECASECLASH 137 /* Filename exists with different case */
215#define EWOULDBLOCK EAGAIN /* Operation would block */
216#define EOVERFLOW 139 /* Value too large for defined data type */
217
218#undef HOST_NOT_FOUND
219#define HOST_NOT_FOUND 1
220#undef TRY_AGAIN
221#define TRY_AGAIN 2
222#undef NO_RECOVERY
223#define NO_RECOVERY 3
224#undef NO_ADDRESS
225#define NO_ADDRESS 4
226
227#define PROT_READ 0x1
228#define PROT_WRITE 0x2
229#define MAP_SHARED 0x1
230#define MAP_PRIVATE 0x2 /* unsupported */
231#define MAP_FIXED 0x10
232#define MAP_FAILED ((void *)-1)
233
234 struct statfs
235 {
236 long f_type; /* type of filesystem (see below) */
237 long f_bsize; /* optimal transfer block size */
238 long f_blocks; /* total data blocks in file system */
239 long f_bfree; /* free blocks in fs */
240 long f_bavail; /* free blocks avail to non-superuser */
241 long f_files; /* total file nodes in file system */
242 long f_ffree; /* free file nodes in fs */
243 long f_fsid; /* file system id */
244 long f_namelen; /* maximum length of filenames */
245 long f_spare[6]; /* spare for later */
246 };
247
248 extern const struct in6_addr in6addr_any; /* :: */
249 extern const struct in6_addr in6addr_loopback; /* ::1 */
250
251/* Taken from the Wine project <http://www.winehq.org>
252 /wine/include/winternl.h */
253 enum SYSTEM_INFORMATION_CLASS
254 {
255 SystemBasicInformation = 0,
256 Unknown1,
257 SystemPerformanceInformation = 2,
258 SystemTimeOfDayInformation = 3, /* was SystemTimeInformation */
259 Unknown4,
260 SystemProcessInformation = 5,
261 Unknown6,
262 Unknown7,
263 SystemProcessorPerformanceInformation = 8,
264 Unknown9,
265 Unknown10,
266 SystemDriverInformation,
267 Unknown12,
268 Unknown13,
269 Unknown14,
270 Unknown15,
271 SystemHandleList,
272 Unknown17,
273 Unknown18,
274 Unknown19,
275 Unknown20,
276 SystemCacheInformation,
277 Unknown22,
278 SystemInterruptInformation = 23,
279 SystemExceptionInformation = 33,
280 SystemRegistryQuotaInformation = 37,
281 SystemLookasideInformation = 45
282 };
283
284 typedef struct
285 {
286 LARGE_INTEGER IdleTime;
287 LARGE_INTEGER KernelTime;
288 LARGE_INTEGER UserTime;
289 LARGE_INTEGER Reserved1[2];
290 ULONG Reserved2;
291 } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
292
293#define sleep(secs) (Sleep(secs * 1000))
294
295/*********************** statfs *****************************/
296/* fake block size */
297#define FAKED_BLOCK_SIZE 512
298
299/* linux-compatible values for fs type */
300#define MSDOS_SUPER_MAGIC 0x4d44
301#define NTFS_SUPER_MAGIC 0x5346544E
302
303/*********************** End of statfs ***********************/
304
305#define SHUT_RDWR SD_BOTH
306
307/* Operations for flock() */
308#define LOCK_SH 1 /* shared lock */
309#define LOCK_EX 2 /* exclusive lock */
310#define LOCK_NB 4 /* or'd with one of the above to prevent
311 * blocking */
312#define LOCK_UN 8 /* remove lock */
313
314/* Not supported under MinGW */
315#define S_IRGRP 0
316#define S_IWGRP 0
317#define S_IROTH 0
318#define S_IXGRP 0
319#define S_IWOTH 0
320#define S_IXOTH 0
321#define S_ISUID 0
322#define S_ISGID 0
323#define S_ISVTX 0
324#define S_IRWXG 0
325#define S_IRWXO 0
326
327#define SHUT_WR SD_SEND
328#define SHUT_RD SD_RECEIVE
329#define SHUT_RDWR SD_BOTH
330
331#define SIGKILL 9
332#define SIGTERM 15
333
334#define SetErrnoFromWinError(e) _SetErrnoFromWinError(e, __FILE__, __LINE__)
335
336 BOOL _plibc_CreateShortcut (const char *pszSrc, const char *pszDest);
337 BOOL _plibc_DereferenceShortcut (char *pszShortcut);
338 char *plibc_ChooseDir (char *pszTitle, unsigned long ulFlags);
339 char *plibc_ChooseFile (char *pszTitle, unsigned long ulFlags);
340 long QueryRegistry (HKEY hMainKey, const char *pszKey, const char *pszSubKey,
341 char *pszBuffer, long *pdLength);
342
343 BOOL __win_IsHandleMarkedAsBlocking (int hHandle);
344 void __win_SetHandleBlockingMode (int s, BOOL bBlocking);
345 void __win_DiscardHandleBlockingMode (int s);
346 int _win_isSocketValid (int s);
347 int plibc_conv_to_win_path (const char *pszUnix, char *pszWindows);
348 unsigned plibc_get_handle_count ();
349
350 typedef void (*TPanicProc) (int, char *);
351 void plibc_set_panic_proc (TPanicProc proc);
352
353 int flock (int fd, int operation);
354 int fsync (int fildes);
355 int inet_pton (int af, const char *src, void *dst);
356 int inet_pton4 (const char *src, u_char * dst, int pton);
357#if USE_IPV6
358 int inet_pton6 (const char *src, u_char * dst);
359#endif
360 int truncate (const char *fname, int distance);
361 int statfs (const char *path, struct statfs *buf);
362 const char *hstrerror (int err);
363 int mkstemp (char *tmplate);
364 char *strptime (const char *buf, const char *format, struct tm *tm);
365 const char *inet_ntop (int af, const void *src, char *dst, size_t size);
366
367 int plibc_init (char *pszOrg, char *pszApp);
368 void plibc_shutdown ();
369 int plibc_initialized ();
370 int plibc_conv_to_win_path_ex (const char *pszUnix, char *pszWindows,
371 int derefLinks);
372 void _SetErrnoFromWinError (long lWinError, char *pszCaller, int iLine);
373 void SetErrnoFromWinsockError (long lWinError);
374 void SetHErrnoFromWinError (long lWinError);
375 void SetErrnoFromHRESULT (HRESULT hRes);
376 int GetErrnoFromWinsockError (long lWinError);
377 FILE *_win_fopen (const char *filename, const char *mode);
378 DIR *_win_opendir (const char *dirname);
379 int _win_open (const char *filename, int oflag, ...);
380#ifdef ENABLE_NLS
381 char *_win_bindtextdomain (const char *domainname, const char *dirname);
382#endif
383 int _win_chdir (const char *path);
384 int _win_close (int fd);
385 int _win_creat (const char *path, mode_t mode);
386 char *_win_ctime (const time_t * clock);
387 char *_win_ctime_r (const time_t * clock, char *buf);
388 int _win_fstat (int handle, struct stat *buffer);
389 int _win_ftruncate (int fildes, off_t length);
390 void _win_gettimeofday (struct timeval *tp, void *tzp);
391 int _win_kill (pid_t pid, int sig);
392 int _win_pipe (int *phandles);
393 int _win_rmdir (const char *path);
394 int _win_access (const char *path, int mode);
395 int _win_chmod (const char *filename, int pmode);
396 char *realpath (const char *file_name, char *resolved_name);
397 long _win_random (void);
398 void _win_srandom (unsigned int seed);
399 int _win_remove (const char *path);
400 int _win_rename (const char *oldname, const char *newname);
401 int _win_stat (const char *path, struct stat *buffer);
402 int _win_stat64 (const char *path, struct stat64 *buffer);
403 long _win_sysconf (int name);
404 int _win_unlink (const char *filename);
405 int _win_write (int fildes, const void *buf, size_t nbyte);
406 int _win_read (int fildes, void *buf, size_t nbyte);
407 size_t _win_fwrite (const void *buffer, size_t size, size_t count,
408 FILE * stream);
409 size_t _win_fread (void *buffer, size_t size, size_t count, FILE * stream);
410 int _win_symlink (const char *path1, const char *path2);
411 void *_win_mmap (void *start, size_t len, int access, int flags, int fd,
412 unsigned long long offset);
413 int _win_munmap (void *start, size_t length);
414 int _win_lstat (const char *path, struct stat *buf);
415 int _win_lstat64 (const char *path, struct stat64 *buf);
416 int _win_readlink (const char *path, char *buf, size_t bufsize);
417 int _win_accept (int s, struct sockaddr *addr, int *addrlen);
418 int _win_printf (const char *format, ...);
419 int _win_fprintf (FILE * f, const char *format, ...);
420 int _win_vprintf (const char *format, va_list ap);
421 int _win_vfprintf (FILE * stream, const char *format, va_list arg_ptr);
422 int _win_vsprintf (char *dest, const char *format, va_list arg_ptr);
423 int _win_vsnprintf (char *str, size_t size, const char *format,
424 va_list arg_ptr);
425 int _win_snprintf (char *str, size_t size, const char *format, ...);
426 int _win_sprintf (char *dest, const char *format, ...);
427 int _win_vsscanf (const char *str, const char *format, va_list arg_ptr);
428 int _win_sscanf (const char *str, const char *format, ...);
429 int _win_vfscanf (FILE * stream, const char *format, va_list arg_ptr);
430 int _win_vscanf (const char *format, va_list arg_ptr);
431 int _win_scanf (const char *format, ...);
432 int _win_fscanf (FILE * stream, const char *format, ...);
433 pid_t _win_waitpid (pid_t pid, int *stat_loc, int options);
434 int _win_bind (int s, const struct sockaddr *name, int namelen);
435 int _win_connect (int s, const struct sockaddr *name, int namelen);
436 int _win_getpeername (int s, struct sockaddr *name, int *namelen);
437 int _win_getsockname (int s, struct sockaddr *name, int *namelen);
438 int _win_getsockopt (int s, int level, int optname, char *optval,
439 int *optlen);
440 int _win_listen (int s, int backlog);
441 int _win_recv (int s, char *buf, int len, int flags);
442 int _win_recvfrom (int s, void *buf, int len, int flags,
443 struct sockaddr *from, int *fromlen);
444 int _win_select (int max_fd, fd_set * rfds, fd_set * wfds, fd_set * efds,
445 const struct timeval *tv);
446 int _win_send (int s, const char *buf, int len, int flags);
447 int _win_sendto (int s, const char *buf, int len, int flags,
448 const struct sockaddr *to, int tolen);
449 int _win_setsockopt (int s, int level, int optname, const void *optval,
450 int optlen);
451 int _win_shutdown (int s, int how);
452 int _win_socket (int af, int type, int protocol);
453 struct hostent *_win_gethostbyaddr (const char *addr, int len, int type);
454 struct hostent *_win_gethostbyname (const char *name);
455 struct hostent *gethostbyname2 (const char *name, int af);
456 char *_win_strerror (int errnum);
457 int IsWinNT ();
458 char *index (const char *s, int c);
459
460#if !HAVE_STRNDUP
461 char *strndup (const char *s, size_t n);
462#endif
463#if !HAVE_STRNLEN
464 size_t strnlen (const char *str, size_t maxlen);
465#endif
466 char *stpcpy (char *dest, const char *src);
467 char *strcasestr (const char *haystack_start, const char *needle_start);
468
469#define strcasecmp(a, b) stricmp(a, b)
470#define strncasecmp(a, b, c) strnicmp(a, b, c)
471
472#endif /* WINDOWS */
473
474#ifndef WINDOWS
475#define DIR_SEPARATOR '/'
476#define DIR_SEPARATOR_STR "/"
477#define PATH_SEPARATOR ':'
478#define PATH_SEPARATOR_STR ":"
479#define NEWLINE "\n"
480
481#ifdef ENABLE_NLS
482#define BINDTEXTDOMAIN(d, n) bindtextdomain(d, n)
483#endif
484#define CREAT(p, m) creat(p, m)
485#define PLIBC_CTIME(c) ctime(c)
486#define CTIME_R(c, b) ctime_r(c, b)
487#undef FOPEN
488#define FOPEN(f, m) fopen(f, m)
489#define FTRUNCATE(f, l) ftruncate(f, l)
490#define OPENDIR(d) opendir(d)
491#define OPEN open
492#define CHDIR(d) chdir(d)
493#define CLOSE(f) close(f)
494#define LSEEK(f, o, w) lseek(f, o, w)
495#define RMDIR(f) rmdir(f)
496#define ACCESS(p, m) access(p, m)
497#define CHMOD(f, p) chmod(f, p)
498#define FSTAT(h, b) fstat(h, b)
499#define PLIBC_KILL(p, s) kill(p, s)
500#define PIPE(h) pipe(h)
501#define REMOVE(p) remove(p)
502#define RENAME(o, n) rename(o, n)
503#define STAT(p, b) stat(p, b)
504#define STAT64(p, b) stat64(p, b)
505#define SYSCONF(n) sysconf(n)
506#define UNLINK(f) unlink(f)
507#define WRITE(f, b, n) write(f, b, n)
508#define READ(f, b, n) read(f, b, n)
509#define GN_FREAD(b, s, c, f) fread(b, s, c, f)
510#define GN_FWRITE(b, s, c, f) fwrite(b, s, c, f)
511#define SYMLINK(a, b) symlink(a, b)
512#define MMAP(s, l, p, f, d, o) mmap(s, l, p, f, d, o)
513#define MUNMAP(s, l) munmap(s, l)
514#define STRERROR(i) strerror(i)
515#define RANDOM() random()
516#define SRANDOM(s) srandom(s)
517#define READLINK(p, b, s) readlink(p, b, s)
518#define LSTAT(p, b) lstat(p, b)
519#define LSTAT64(p, b) lstat64(p, b)
520#define PRINTF printf
521#define FPRINTF fprintf
522#define VPRINTF(f, a) vprintf(f, a)
523#define VFPRINTF(s, f, a) vfprintf(s, f, a)
524#define VSPRINTF(d, f, a) vsprintf(d, f, a)
525#define VSNPRINTF(str, size, fmt, a) vsnprintf(str, size, fmt, a)
526#define _REAL_SNPRINTF snprintf
527#define SPRINTF sprintf
528#define VSSCANF(s, f, a) vsscanf(s, f, a)
529#define SSCANF sscanf
530#define VFSCANF(s, f, a) vfscanf(s, f, a)
531#define VSCANF(f, a) vscanf(f, a)
532#define SCANF scanf
533#define FSCANF fscanf
534#define WAITPID(p, s, o) waitpid(p, s, o)
535#define ACCEPT(s, a, l) accept(s, a, l)
536#define BIND(s, n, l) bind(s, n, l)
537#define CONNECT(s, n, l) connect(s, n, l)
538#define GETPEERNAME(s, n, l) getpeername(s, n, l)
539#define GETSOCKNAME(s, n, l) getsockname(s, n, l)
540#define GETSOCKOPT(s, l, o, v, p) getsockopt(s, l, o, v, p)
541#define LISTEN(s, b) listen(s, b)
542#define RECV(s, b, l, f) recv(s, b, l, f)
543#define RECVFROM(s, b, l, f, r, o) recvfrom(s, b, l, f, r, o)
544#define SELECT(n, r, w, e, t) select(n, r, w, e, t)
545#define SEND(s, b, l, f) send(s, b, l, f)
546#define SENDTO(s, b, l, f, o, n) sendto(s, b, l, f, o, n)
547#define SETSOCKOPT(s, l, o, v, n) setsockopt(s, l, o, v, n)
548#define SHUTDOWN(s, h) shutdown(s, h)
549#define SOCKET(a, t, p) socket(a, t, p)
550#define GETHOSTBYADDR(a, l, t) gethostbyname(a, l, t)
551#define GETHOSTBYNAME(n) gethostbyname(n)
552#define GETTIMEOFDAY(t, n) gettimeofday(t, n)
553#define INSQUE(e, p) insque(e, p)
554#define REMQUE(e) remque(e)
555#define HSEARCH(i, a) hsearch(i, a)
556#define HCREATE(n) hcreate(n)
557#define HDESTROY() hdestroy()
558#define HSEARCH_R(i, a, r, h) hsearch_r(i, a, r, h)
559#define HCREATE_R(n, h) hcreate_r(n, h)
560#define HDESTROY_R(h) hdestroy_r(h)
561#define TSEARCH(k, r, c) tsearch(k, r, c)
562#define TFIND(k, r, c) tfind(k, r, c)
563#define TDELETE(k, r, c) tdelete(k, r, c)
564#define TWALK(r, a) twalk(r, a)
565#define TDESTROY(r, f) tdestroy(r, f)
566#define LFIND(k, b, n, s, c) lfind(k, b, n, s, c)
567#define LSEARCH(k, b, n, s, c) lsearch(k, b, n, s, c)
568#else
569#define DIR_SEPARATOR '\\'
570#define DIR_SEPARATOR_STR "\\"
571#define PATH_SEPARATOR ';'
572#define PATH_SEPARATOR_STR ";"
573#define NEWLINE "\r\n"
574
575#ifdef ENABLE_NLS
576#define BINDTEXTDOMAIN(d, n) _win_bindtextdomain(d, n)
577#endif
578#define CREAT(p, m) _win_creat(p, m)
579#define PLIBC_CTIME(c) _win_ctime(c)
580#define CTIME_R(c, b) _win_ctime_r(c, b)
581#define FOPEN(f, m) _win_fopen(f, m)
582#define FTRUNCATE(f, l) _win_ftruncate(f, l)
583#define OPENDIR(d) _win_opendir(d)
584#define OPEN _win_open
585#define CHDIR(d) _win_chdir(d)
586#define CLOSE(f) _win_close(f)
587#define PLIBC_KILL(p, s) _win_kill(p, s)
588#define LSEEK(f, o, w) _win_lseek(f, o, w)
589#define FSTAT(h, b) _win_fstat(h, b)
590#define RMDIR(f) _win_rmdir(f)
591#define ACCESS(p, m) _win_access(p, m)
592#define CHMOD(f, p) _win_chmod(f, p)
593#define PIPE(h) _win_pipe(h)
594#define RANDOM() _win_random()
595#define SRANDOM(s) _win_srandom(s)
596#define REMOVE(p) _win_remove(p)
597#define RENAME(o, n) _win_rename(o, n)
598#define STAT(p, b) _win_stat(p, b)
599#define STAT64(p, b) _win_stat64(p, b)
600#define SYSCONF(n) _win_sysconf(n)
601#define UNLINK(f) _win_unlink(f)
602#define WRITE(f, b, n) _win_write(f, b, n)
603#define READ(f, b, n) _win_read(f, b, n)
604#define GN_FREAD(b, s, c, f) _win_fread(b, s, c, f)
605#define GN_FWRITE(b, s, c, f) _win_fwrite(b, s, c, f)
606#define SYMLINK(a, b) _win_symlink(a, b)
607#define MMAP(s, l, p, f, d, o) _win_mmap(s, l, p, f, d, o)
608#define MUNMAP(s, l) _win_munmap(s, l)
609#define STRERROR(i) _win_strerror(i)
610#define READLINK(p, b, s) _win_readlink(p, b, s)
611#define LSTAT(p, b) _win_lstat(p, b)
612#define LSTAT64(p, b) _win_lstat64(p, b)
613#define PRINTF(f, ...) _win_printf(f , __VA_ARGS__)
614#define FPRINTF(fil, fmt, ...) _win_fprintf(fil, fmt, __VA_ARGS__)
615#define VPRINTF(f, a) _win_vprintf(f, a)
616#define VFPRINTF(s, f, a) _win_vfprintf(s, f, a)
617#define VSPRINTF(d, f, a) _win_vsprintf(d, f, a)
618#define VSNPRINTF(str, size, fmt, a) _win_vsnprintf(str, size, fmt, a)
619#define _REAL_SNPRINTF(str, size, fmt, ...) _win_snprintf(str, size, fmt, __VA_ARGS__)
620#define SPRINTF(d, f, ...) _win_sprintf(d, f, __VA_ARGS__)
621#define VSSCANF(s, f, a) _win_vsscanf(s, f, a)
622#define SSCANF(s, f, ...) _win_sscanf(s, f, __VA_ARGS__)
623#define VFSCANF(s, f, a) _win_vfscanf(s, f, a)
624#define VSCANF(f, a) _win_vscanf(f, a)
625#define SCANF(f, ...) _win_scanf(f, __VA_ARGS__)
626#define FSCANF(s, f, ...) _win_fscanf(s, f, __VA_ARGS__)
627#define WAITPID(p, s, o) _win_waitpid(p, s, o)
628#define ACCEPT(s, a, l) _win_accept(s, a, l)
629#define BIND(s, n, l) _win_bind(s, n, l)
630#define CONNECT(s, n, l) _win_connect(s, n, l)
631#define GETPEERNAME(s, n, l) _win_getpeername(s, n, l)
632#define GETSOCKNAME(s, n, l) _win_getsockname(s, n, l)
633#define GETSOCKOPT(s, l, o, v, p) _win_getsockopt(s, l, o, v, p)
634#define LISTEN(s, b) _win_listen(s, b)
635#define RECV(s, b, l, f) _win_recv(s, b, l, f)
636#define RECVFROM(s, b, l, f, r, o) _win_recvfrom(s, b, l, f, r, o)
637#define SELECT(n, r, w, e, t) _win_select(n, r, w, e, t)
638#define SEND(s, b, l, f) _win_send(s, b, l, f)
639#define SENDTO(s, b, l, f, o, n) _win_sendto(s, b, l, f, o, n)
640#define SETSOCKOPT(s, l, o, v, n) _win_setsockopt(s, l, o, v, n)
641#define SHUTDOWN(s, h) _win_shutdown(s, h)
642#define SOCKET(a, t, p) _win_socket(a, t, p)
643#define GETHOSTBYADDR(a, l, t) _win_gethostbyname(a, l, t)
644#define GETHOSTBYNAME(n) _win_gethostbyname(n)
645#define GETTIMEOFDAY(t, n) _win_gettimeofday(t, n)
646#define INSQUE(e, p) _win_insque(e, p)
647#define REMQUE(e) _win_remque(e)
648#define HSEARCH(i, a) _win_hsearch(i, a)
649#define HCREATE(n) _win_hcreate(n)
650#define HDESTROY() _win_hdestroy()
651#define HSEARCH_R(i, a, r, h) _win_hsearch_r(i, a, r, h)
652#define HCREATE_R(n, h) _win_hcreate_r(n, h)
653#define HDESTROY_R(h) _win_hdestroy_r(h)
654#define TSEARCH(k, r, c) _win_tsearch(k, r, c)
655#define TFIND(k, r, c) _win_tfind(k, r, c)
656#define TDELETE(k, r, c) _win_tdelete(k, r, c)
657#define TWALK(r, a) _win_twalk(r, a)
658#define TDESTROY(r, f) _win_tdestroy(r, f)
659#define LFIND(k, b, n, s, c) _win_lfind(k, b, n, s, c)
660#define LSEARCH(k, b, n, s, c) _win_lsearch(k, b, n, s, c)
661#endif
662
663/* search.h */
664
665/* Prototype structure for a linked-list data structure.
666 This is the type used by the `insque' and `remque' functions. */
667
668 struct PLIBC_SEARCH_QELEM
669 {
670 struct qelem *q_forw;
671 struct qelem *q_back;
672 char q_data[1];
673 };
674
675
676/* Insert ELEM into a doubly-linked list, after PREV. */
677 void _win_insque (void *__elem, void *__prev);
678
679/* Unlink ELEM from the doubly-linked list that it is in. */
680 void _win_remque (void *__elem);
681
682
683/* For use with hsearch(3). */
684 typedef int (*PLIBC_SEARCH__compar_fn_t) (__const void *, __const void *);
685
686 typedef PLIBC_SEARCH__compar_fn_t _win_comparison_fn_t;
687
688/* Action which shall be performed in the call the hsearch. */
689 typedef enum
690 {
691 PLIBC_SEARCH_FIND,
692 PLIBC_SEARCH_ENTER
693 }
694 PLIBC_SEARCH_ACTION;
695
696 typedef struct PLIBC_SEARCH_entry
697 {
698 char *key;
699 void *data;
700 }
701 PLIBC_SEARCH_ENTRY;
702
703/* The reentrant version has no static variables to maintain the state.
704 Instead the interface of all functions is extended to take an argument
705 which describes the current status. */
706 typedef struct _PLIBC_SEARCH_ENTRY
707 {
708 unsigned int used;
709 PLIBC_SEARCH_ENTRY entry;
710 }
711 _PLIBC_SEARCH_ENTRY;
712
713
714/* Family of hash table handling functions. The functions also
715 have reentrant counterparts ending with _r. The non-reentrant
716 functions all work on a signle internal hashing table. */
717
718/* Search for entry matching ITEM.key in internal hash table. If
719 ACTION is `FIND' return found entry or signal error by returning
720 NULL. If ACTION is `ENTER' replace existing data (if any) with
721 ITEM.data. */
722 PLIBC_SEARCH_ENTRY *_win_hsearch (PLIBC_SEARCH_ENTRY __item,
723 PLIBC_SEARCH_ACTION __action);
724
725/* Create a new hashing table which will at most contain NEL elements. */
726 int _win_hcreate (size_t __nel);
727
728/* Destroy current internal hashing table. */
729 void _win_hdestroy (void);
730
731/* Data type for reentrant functions. */
732 struct PLIBC_SEARCH_hsearch_data
733 {
734 struct _PLIBC_SEARCH_ENTRY *table;
735 unsigned int size;
736 unsigned int filled;
737 };
738
739/* Reentrant versions which can handle multiple hashing tables at the
740 same time. */
741 int _win_hsearch_r (PLIBC_SEARCH_ENTRY __item, PLIBC_SEARCH_ACTION __action,
742 PLIBC_SEARCH_ENTRY ** __retval,
743 struct PLIBC_SEARCH_hsearch_data *__htab);
744 int _win_hcreate_r (size_t __nel, struct PLIBC_SEARCH_hsearch_data *__htab);
745 void _win_hdestroy_r (struct PLIBC_SEARCH_hsearch_data *__htab);
746
747
748/* The tsearch routines are very interesting. They make many
749 assumptions about the compiler. It assumes that the first field
750 in node must be the "key" field, which points to the datum.
751 Everything depends on that. */
752/* For tsearch */
753 typedef enum
754 {
755 PLIBC_SEARCH_preorder,
756 PLIBC_SEARCH_postorder,
757 PLIBC_SEARCH_endorder,
758 PLIBC_SEARCH_leaf
759 }
760 PLIBC_SEARCH_VISIT;
761
762/* Search for an entry matching the given KEY in the tree pointed to
763 by *ROOTP and insert a new element if not found. */
764 void *_win_tsearch (__const void *__key, void **__rootp,
765 PLIBC_SEARCH__compar_fn_t __compar);
766
767/* Search for an entry matching the given KEY in the tree pointed to
768 by *ROOTP. If no matching entry is available return NULL. */
769 void *_win_tfind (__const void *__key, void *__const * __rootp,
770 PLIBC_SEARCH__compar_fn_t __compar);
771
772/* Remove the element matching KEY from the tree pointed to by *ROOTP. */
773 void *_win_tdelete (__const void *__restrict __key, void **__restrict __rootp,
774 PLIBC_SEARCH__compar_fn_t __compar);
775
776 typedef void (*PLIBC_SEARCH__action_fn_t) (__const void *__nodep,
777 PLIBC_SEARCH_VISIT __value,
778 int __level);
779
780/* Walk through the whole tree and call the ACTION callback for every node
781 or leaf. */
782 void _win_twalk (__const void *__root, PLIBC_SEARCH__action_fn_t __action);
783
784/* Callback type for function to free a tree node. If the keys are atomic
785 data this function should do nothing. */
786 typedef void (*PLIBC_SEARCH__free_fn_t) (void *__nodep);
787
788/* Destroy the whole tree, call FREEFCT for each node or leaf. */
789 void _win_tdestroy (void *__root, PLIBC_SEARCH__free_fn_t __freefct);
790
791
792/* Perform linear search for KEY by comparing by COMPAR in an array
793 [BASE,BASE+NMEMB*SIZE). */
794 void *_win_lfind (__const void *__key, __const void *__base, size_t * __nmemb,
795 size_t __size, PLIBC_SEARCH__compar_fn_t __compar);
796
797/* Perform linear search for KEY by comparing by COMPAR function in
798 array [BASE,BASE+NMEMB*SIZE) and insert entry if not found. */
799 void *_win_lsearch (__const void *__key, void *__base, size_t * __nmemb,
800 size_t __size, PLIBC_SEARCH__compar_fn_t __compar);
801
802
803#ifdef __cplusplus
804}
805#endif
806
807
808#endif //_PLIBC_H_
809
810/* end of plibc.h */
diff --git a/pathologist/src/mi/Makefile.am b/pathologist/src/mi/Makefile.am
new file mode 100644
index 0000000..1248080
--- /dev/null
+++ b/pathologist/src/mi/Makefile.am
@@ -0,0 +1,29 @@
1INCLUDES = -I$(top_srcdir)/src/include -I$(top_builddir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = --coverage -O0
9 XLIB = -lgcov
10endif
11
12lib_LTLIBRARIES = libmi.la
13
14libmi_la_SOURCES = \
15 gdbmi_alloc.c \
16 gdbmi_breakpoint.c \
17 gdbmi_connect.c \
18 gdbmi_data_man.c \
19 gdbmi_error.c \
20 gdbmi_get_free_pty.c \
21 gdbmi_get_free_vt.c \
22 gdbmi_misc.c \
23 gdbmi_parse.c \
24 gdbmi_prg_control.c \
25 gdbmi_stack_man.c \
26 gdbmi_symbol_query.c \
27 gdbmi_target_man.c \
28 gdbmi_thread.c \
29 gdbmi_var_obj.c \ No newline at end of file
diff --git a/pathologist/src/mi/gdbmi_alloc.c b/pathologist/src/mi/gdbmi_alloc.c
new file mode 100644
index 0000000..0fe6e14
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_alloc.c
@@ -0,0 +1,308 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: Allocator.
7 Comments:
8 Most alloc/free routines are here. Free routines must accept NULL
9pointers. Alloc functions must set mi_error. @<p>
10
11***************************************************************************/
12
13#include "gdbmi.h"
14
15void *mi_calloc(size_t count, size_t sz)
16{
17 void *res=calloc(count,sz);
18 if (!res)
19 mi_error=MI_OUT_OF_MEMORY;
20 return res;
21}
22
23void *mi_calloc1(size_t sz)
24{
25 return mi_calloc(1,sz);
26}
27
28char *mi_malloc(size_t sz)
29{
30 char *res=malloc(sz);
31 if (!res)
32 mi_error=MI_OUT_OF_MEMORY;
33 return res;
34}
35
36mi_results *mi_alloc_results(void)
37{
38 return (mi_results *)mi_calloc1(sizeof(mi_results));
39}
40
41mi_output *mi_alloc_output(void)
42{
43 return (mi_output *)mi_calloc1(sizeof(mi_output));
44}
45
46mi_frames *mi_alloc_frames(void)
47{
48 return (mi_frames *)mi_calloc1(sizeof(mi_frames));
49}
50
51mi_gvar *mi_alloc_gvar(void)
52{
53 return (mi_gvar *)mi_calloc1(sizeof(mi_gvar));
54}
55
56mi_gvar_chg *mi_alloc_gvar_chg(void)
57{
58 return (mi_gvar_chg *)mi_calloc1(sizeof(mi_gvar_chg));
59}
60
61mi_bkpt *mi_alloc_bkpt(void)
62{
63 mi_bkpt *b=(mi_bkpt *)mi_calloc1(sizeof(mi_bkpt));
64 if (b)
65 {
66 b->thread=-1;
67 b->ignore=-1;
68 }
69 return b;
70}
71
72mi_wp *mi_alloc_wp(void)
73{
74 return (mi_wp *)mi_calloc1(sizeof(mi_wp));
75}
76
77mi_stop *mi_alloc_stop(void)
78{
79 return (mi_stop *)mi_calloc1(sizeof(mi_stop));
80}
81
82mi_asm_insns *mi_alloc_asm_insns(void)
83{
84 return (mi_asm_insns *)mi_calloc1(sizeof(mi_asm_insns));
85}
86
87mi_asm_insn *mi_alloc_asm_insn(void)
88{
89 return (mi_asm_insn *)mi_calloc1(sizeof(mi_asm_insn));
90}
91
92mi_chg_reg *mi_alloc_chg_reg(void)
93{
94 return (mi_chg_reg *)mi_calloc1(sizeof(mi_chg_reg));
95}
96
97/*****************************************************************************
98 Free functions
99*****************************************************************************/
100
101void mi_free_frames(mi_frames *f)
102{
103 mi_frames *aux;
104
105 while (f)
106 {
107 free(f->func);
108 free(f->file);
109 free(f->from);
110 mi_free_results(f->args);
111 aux=f->next;
112 free(f);
113 f=aux;
114 }
115}
116
117void mi_free_bkpt(mi_bkpt *b)
118{
119 mi_bkpt *aux;
120
121 while (b)
122 {
123 free(b->func);
124 free(b->file);
125 free(b->file_abs);
126 free(b->cond);
127 aux=b->next;
128 free(b);
129 b=aux;
130 }
131}
132
133void mi_free_gvar(mi_gvar *v)
134{
135 mi_gvar *aux;
136
137 while (v)
138 {
139 free(v->name);
140 free(v->type);
141 free(v->exp);
142 free(v->value);
143 if (v->numchild && v->child)
144 mi_free_gvar(v->child);
145 aux=v->next;
146 free(v);
147 v=aux;
148 }
149}
150
151void mi_free_gvar_chg(mi_gvar_chg *p)
152{
153 mi_gvar_chg *aux;
154
155 while (p)
156 {
157 free(p->name);
158 free(p->new_type);
159 aux=p->next;
160 free(p);
161 p=aux;
162 }
163}
164
165void mi_free_results_but(mi_results *r, mi_results *no)
166{
167 mi_results *aux;
168
169 while (r)
170 {
171 if (r==no)
172 {
173 aux=r->next;
174 r->next=NULL;
175 r=aux;
176 }
177 else
178 {
179 free(r->var);
180 switch (r->type)
181 {
182 case t_const:
183 free(r->v.cstr);
184 break;
185 case t_tuple:
186 case t_list:
187 mi_free_results_but(r->v.rs,no);
188 break;
189 }
190 aux=r->next;
191 free(r);
192 r=aux;
193 }
194 }
195}
196
197void mi_free_results(mi_results *r)
198{
199 mi_free_results_but(r,NULL);
200}
201
202void mi_free_output_but(mi_output *r, mi_output *no, mi_results *no_r)
203{
204 mi_output *aux;
205
206 while (r)
207 {
208 if (r==no)
209 {
210 aux=r->next;
211 r->next=NULL;
212 r=aux;
213 }
214 else
215 {
216 if (r->c)
217 mi_free_results_but(r->c,no_r);
218 aux=r->next;
219 free(r);
220 r=aux;
221 }
222 }
223}
224
225void mi_free_output(mi_output *r)
226{
227 mi_free_output_but(r,NULL,NULL);
228}
229
230void mi_free_stop(mi_stop *s)
231{
232 if (!s)
233 return;
234 mi_free_frames(s->frame);
235 mi_free_wp(s->wp);
236 free(s->wp_old);
237 free(s->wp_val);
238 free(s->gdb_result_var);
239 free(s->return_value);
240 free(s->signal_name);
241 free(s->signal_meaning);
242 free(s);
243}
244
245void mi_free_wp(mi_wp *wp)
246{
247 mi_wp *aux;
248 while (wp)
249 {
250 free(wp->exp);
251 aux=wp->next;
252 free(wp);
253 wp=aux;
254 }
255}
256
257void mi_free_asm_insns(mi_asm_insns *i)
258{
259 mi_asm_insns *aux;
260
261 while (i)
262 {
263 free(i->file);
264 mi_free_asm_insn(i->ins);
265 aux=i->next;
266 free(i);
267 i=aux;
268 }
269}
270
271void mi_free_asm_insn(mi_asm_insn *i)
272{
273 mi_asm_insn *aux;
274
275 while (i)
276 {
277 free(i->func);
278 free(i->inst);
279 aux=i->next;
280 free(i);
281 i=aux;
282 }
283}
284
285/*void mi_free_charp_list(char **l)
286{
287 char **c=l;
288 while (c)
289 {
290 free(*c);
291 c++;
292 }
293 free(l);
294}*/
295
296void mi_free_chg_reg(mi_chg_reg *r)
297{
298 mi_chg_reg *aux;
299 while (r)
300 {
301 free(r->val);
302 free(r->name);
303 aux=r->next;
304 free(r);
305 r=aux;
306 }
307}
308
diff --git a/pathologist/src/mi/gdbmi_breakpoint.c b/pathologist/src/mi/gdbmi_breakpoint.c
new file mode 100644
index 0000000..f91cc3e
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_breakpoint.c
@@ -0,0 +1,265 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: Breakpoint table commands.
7 Comments:
8 GDB/MI commands for the "Breakpoint Table Commands" section.
9 @<p>
10@<pre>
11gdb command: Implemented?
12
13-break-after Yes
14-break-condition Yes
15-break-delete Yes
16-break-disable Yes
17-break-enable Yes
18-break-info N.A. (info break NUMBER) (*)
19-break-insert Yes
20-break-list No (*)
21-break-watch Yes
22@</pre>
23
24(*) I think the program should keep track of the breakpoints, so it will
25be implemented when I have more time. @<p>
26
27***************************************************************************/
28
29#include "gdbmi.h"
30
31/* Low level versions. */
32
33void mi_break_insert_fl(mi_h *h, const char *file, int line)
34{
35 mi_send(h,"-break-insert %s:%d\n",file,line);
36}
37
38void mi_break_insert(mi_h *h, int temporary, int hard_assist,
39 const char *cond, int count, int thread,
40 const char *where)
41{
42 char s_count[32];
43 char s_thread[32];
44
45 if (count>=0)
46 snprintf(s_count,32,"%d",count);
47 if (thread>=0)
48 snprintf(s_thread,32,"%d",thread);
49 if (cond)
50 // Conditions may contain spaces, in fact, if they don't gdb will add
51 // them after parsing. Enclosing the expression with "" solves the
52 // problem.
53 mi_send(h,"-break-insert %s %s -c \"%s\" %s %s %s %s %s\n",
54 temporary ? "-t" : "",
55 hard_assist ? "-h" : "",
56 cond,
57 count>=0 ? "-i" : "", count>=0 ? s_count : "",
58 thread>=0 ? "-p" : "", thread>=0 ? s_thread : "",
59 where);
60 else
61 mi_send(h,"-break-insert %s %s %s %s %s %s %s\n",
62 temporary ? "-t" : "",
63 hard_assist ? "-h" : "",
64 count>=0 ? "-i" : "", count>=0 ? s_count : "",
65 thread>=0 ? "-p" : "", thread>=0 ? s_thread : "",
66 where);
67}
68
69void mi_break_insert_flf(mi_h *h, const char *file, int line, int temporary,
70 int hard_assist, const char *cond, int count,
71 int thread)
72{
73 char s_count[32];
74 char s_thread[32];
75
76 if (count>=0)
77 snprintf(s_count,32,"%d",count);
78 if (thread>=0)
79 snprintf(s_thread,32,"%d",thread);
80 mi_send(h,"-break-insert %s %s %s %s %s %s %s %s %s:%d\n",
81 temporary ? "-t" : "",
82 hard_assist ? "-h" : "",
83 cond ? "-c" : "", cond ? cond : "",
84 count>=0 ? "-i" : "", count>=0 ? s_count : "",
85 thread>=0 ? "-p" : "", thread>=0 ? s_thread : "",
86 file,line);
87}
88
89void mi_break_delete(mi_h *h, int number)
90{
91 mi_send(h,"-break-delete %d\n",number);
92}
93
94void mi_break_after(mi_h *h, int number, int count)
95{
96 mi_send(h,"-break-after %d %d\n",number,count);
97}
98
99void mi_break_condition(mi_h *h, int number, const char *condition)
100{
101 mi_send(h,"-break-condition %d %s\n",number,condition);
102}
103
104void mi_break_enable(mi_h *h, int number)
105{
106 mi_send(h,"-break-enable %d\n",number);
107}
108
109void mi_break_disable(mi_h *h, int number)
110{
111 mi_send(h,"-break-disable %d\n",number);
112}
113
114void mi_break_watch(mi_h *h, enum mi_wp_mode mode, const char *exp)
115{
116 if (mode==wm_write)
117 mi_send(h,"-break-watch \"%s\"\n",exp);
118 else
119 mi_send(h,"-break-watch -%c \"%s\"\n",mode==wm_rw ? 'a' : 'r',exp);
120}
121
122/* High level versions. */
123
124/**[txh]********************************************************************
125
126 Description:
127 Insert a breakpoint at file:line.
128
129 Command: -break-insert file:line
130 Return: A new mi_bkpt structure with info about the breakpoint. NULL on
131error.
132
133***************************************************************************/
134
135mi_bkpt *gmi_break_insert(mi_h *h, const char *file, int line)
136{
137 mi_break_insert_fl(h,file,line);
138 return mi_res_bkpt(h);
139}
140
141/**[txh]********************************************************************
142
143 Description:
144 Insert a breakpoint, all available options.
145
146 Command: -break-insert
147 Return: A new mi_bkpt structure with info about the breakpoint. NULL on
148error.
149
150***************************************************************************/
151
152mi_bkpt *gmi_break_insert_full(mi_h *h, int temporary, int hard_assist,
153 const char *cond, int count, int thread,
154 const char *where)
155{
156 mi_break_insert(h,temporary,hard_assist,cond,count,thread,where);
157 return mi_res_bkpt(h);
158}
159
160/**[txh]********************************************************************
161
162 Description:
163 Insert a breakpoint, all available options.
164
165 Command: -break-insert [ops] file:line
166 Return: A new mi_bkpt structure with info about the breakpoint. NULL on
167error.
168
169***************************************************************************/
170
171mi_bkpt *gmi_break_insert_full_fl(mi_h *h, const char *file, int line,
172 int temporary, int hard_assist,
173 const char *cond, int count, int thread)
174{
175 mi_break_insert_flf(h,file,line,temporary,hard_assist,cond,count,thread);
176 return mi_res_bkpt(h);
177}
178
179/**[txh]********************************************************************
180
181 Description:
182 Remove a breakpoint.
183
184 Command: -break-delete
185 Return: !=0 OK. Note that gdb always says OK, but errors can be sent to the
186console.
187
188***************************************************************************/
189
190int gmi_break_delete(mi_h *h, int number)
191{
192 mi_break_delete(h,number);
193 return mi_res_simple_done(h);
194}
195
196/**[txh]********************************************************************
197
198 Description:
199 Modify the "ignore" count for a breakpoint.
200
201 Command: -break-after
202 Return: !=0 OK. Note that gdb always says OK, but errors can be sent to the
203console.
204
205***************************************************************************/
206
207int gmi_break_set_times(mi_h *h, int number, int count)
208{
209 mi_break_after(h,number,count);
210 return mi_res_simple_done(h);
211}
212
213/**[txh]********************************************************************
214
215 Description:
216 Associate a condition with the breakpoint.
217
218 Command: -break-condition
219 Return: !=0 OK
220
221***************************************************************************/
222
223int gmi_break_set_condition(mi_h *h, int number, const char *condition)
224{
225 mi_break_condition(h,number,condition);
226 return mi_res_simple_done(h);
227}
228
229/**[txh]********************************************************************
230
231 Description:
232 Enable or disable a breakpoint.
233
234 Command: -break-enable + -break-disable
235 Return: !=0 OK. Note that gdb always says OK, but errors can be sent to the
236console.
237
238***************************************************************************/
239
240int gmi_break_state(mi_h *h, int number, int enable)
241{
242 if (enable)
243 mi_break_enable(h,number);
244 else
245 mi_break_disable(h,number);
246 return mi_res_simple_done(h);
247}
248
249/**[txh]********************************************************************
250
251 Description:
252 Set a watchpoint. It doesn't work for remote targets!
253
254 Command: -break-watch
255 Return: A new mi_wp structure with info about the watchpoint. NULL on
256error.
257
258***************************************************************************/
259
260mi_wp *gmi_break_watch(mi_h *h, enum mi_wp_mode mode, const char *exp)
261{
262 mi_break_watch(h,mode,exp);
263 return mi_res_wp(h);
264}
265
diff --git a/pathologist/src/mi/gdbmi_connect.c b/pathologist/src/mi/gdbmi_connect.c
new file mode 100644
index 0000000..b9f3754
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_connect.c
@@ -0,0 +1,885 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004-2009 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: Connect.
7 Comments:
8 This module handles the dialog with gdb, including starting and stopping
9gdb.
10 @<p>
11
12GDB Bug workaround for "file -readnow": I tried to workaround a bug using
13it but looks like this option also have bugs!!!! so I have to use the
14command line option --readnow.
15It also have a bug!!!! when the binary is changed and gdb must reload it
16this option is ignored. So it looks like we have no solution but 3 gdb bugs
17in a row.
18
19***************************************************************************/
20
21#include <sys/types.h>
22#include <unistd.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <sys/wait.h>
26#include <fcntl.h>
27#include <string.h>
28#include <stdarg.h>
29#include <limits.h>
30#include <errno.h>
31#include <signal.h>
32#include <sys/stat.h>
33#include <sys/time.h>
34#include "plibc.h"
35
36#include "gdbmi.h"
37#ifndef _GNU_SOURCE
38#define _GNU_SOURCE
39#endif
40
41#ifndef TEMP_FAILURE_RETRY
42 #define TEMP_FAILURE_RETRY(a) (a)
43#endif
44
45int mi_error=MI_OK;
46char *mi_error_from_gdb=NULL;
47static char *gdb_exe=NULL;
48static char *xterm_exe=NULL;
49static char *gdb_start=NULL;
50static char *gdb_conn=NULL;
51static char *main_func=NULL;
52static char disable_psym_search_workaround=0;
53
54mi_h *mi_alloc_h()
55{
56 mi_h *h=(mi_h *)calloc(1,sizeof(mi_h));
57 if (!h)
58 {
59 mi_error=MI_OUT_OF_MEMORY;
60 return NULL;
61 }
62 h->to_gdb[0]=h->to_gdb[1]=h->from_gdb[0]=h->from_gdb[1]=-1;
63 h->pid=-1;
64 return h;
65}
66
67int mi_check_running_pid(pid_t pid)
68{
69 int status;
70
71 if (pid<=0)
72 return 0;
73 /* If waitpid returns the number of our child means it communicated
74 to as a termination status. */
75 if (waitpid(pid,&status,WNOHANG)==pid)
76 {
77 pid=0;
78 return 0;
79 }
80 return 1;
81}
82
83int mi_check_running(mi_h *h)
84{
85 return !h->died && mi_check_running_pid(h->pid);
86}
87
88void mi_kill_child(pid_t pid)
89{
90 kill(pid,SIGTERM);
91 usleep(100000);
92 if (mi_check_running_pid(pid))
93 {
94 int status;
95 kill(pid,SIGKILL);
96 waitpid(pid,&status,0);
97 }
98}
99
100void mi_free_h(mi_h **handle)
101{
102 mi_h *h=*handle;
103 if (h->to_gdb[0]>=0)
104 close(h->to_gdb[0]);
105 if (h->to)
106 fclose(h->to);
107 else if (h->to_gdb[1]>=0)
108 close(h->to_gdb[1]);
109 if (h->from)
110 fclose(h->from);
111 else if (h->from_gdb[0]>=0)
112 close(h->from_gdb[0]);
113 if (h->from_gdb[1]>=0)
114 close(h->from_gdb[1]);
115 if (mi_check_running(h))
116 {/* GDB is running! */
117 mi_kill_child(h->pid);
118 }
119 if (h->line)
120 free(h->line);
121 mi_free_output(h->po);
122 free(h->catched_console);
123 free(h);
124 *handle=NULL;
125}
126
127void mi_set_nonblk(int h)
128{
129 int flf;
130 flf=fcntl(h,F_GETFL,0);
131 flf=flf | O_NONBLOCK;
132 fcntl(h,F_SETFL,flf);
133}
134
135int mi_getline(mi_h *h)
136{
137 char c;
138
139 while (read(h->from_gdb[0],&c,1)==1)
140 {
141 if (h->lread>=h->llen)
142 {
143 h->llen=h->lread+128;
144 h->line=(char *)realloc(h->line,h->llen);
145 if (!h->line)
146 {
147 h->llen=0;
148 h->lread=0;
149 return -1;
150 }
151 }
152 if (c=='\n')
153 {
154 int ret=h->lread;
155 h->line[ret]=0;
156 h->lread=0;
157 return ret;
158 }
159 h->line[h->lread]=c;
160 h->lread++;
161 }
162 return 0;
163}
164
165char *get_cstr(mi_output *o)
166{
167 if (!o->c || o->c->type!=t_const)
168 return NULL;
169 return o->c->v.cstr;
170}
171
172int mi_get_response(mi_h *h)
173{
174 int l=mi_getline(h);
175 if (!l)
176 return 0;
177
178 if (h->from_gdb_echo)
179 h->from_gdb_echo(h->line,h->from_gdb_echo_data);
180 if (strncmp(h->line,"(gdb)",5)==0)
181 {/* End of response. */
182 return 1;
183 }
184 else
185 {/* Add to the response. */
186 mi_output *o;
187 int add=1, is_exit=0;
188 o=mi_parse_gdb_output(h->line);
189
190 if (!o)
191 return 0;
192 /* Tunneled streams callbacks. */
193 if (o->type==MI_T_OUT_OF_BAND && o->stype==MI_ST_STREAM)
194 {
195 char *aux;
196 add=0;
197 switch (o->sstype)
198 {
199 case MI_SST_CONSOLE:
200 aux=get_cstr(o);
201 if (h->console)
202 h->console(aux,h->console_data);
203 if (h->catch_console && aux)
204 {
205 h->catch_console--;
206 if (!h->catch_console)
207 {
208 free(h->catched_console);
209 h->catched_console=strdup(aux);
210 }
211 }
212 break;
213 case MI_SST_TARGET:
214 /* This one seems to be useless. */
215 if (h->target)
216 h->target(get_cstr(o),h->target_data);
217 break;
218 case MI_SST_LOG:
219 if (h->log)
220 h->log(get_cstr(o),h->log_data);
221 break;
222 }
223 }
224 else if (o->type==MI_T_OUT_OF_BAND && o->stype==MI_ST_ASYNC)
225 {
226 if (h->async)
227 h->async(o,h->async_data);
228 }
229 else if (o->type==MI_T_RESULT_RECORD && o->tclass==MI_CL_ERROR)
230 {/* Error from gdb, record it. */
231 mi_error=MI_FROM_GDB;
232 free(mi_error_from_gdb);
233 mi_error_from_gdb=NULL;
234 if (o->c && strcmp(o->c->var,"msg")==0 && o->c->type==t_const)
235 mi_error_from_gdb=strdup(o->c->v.cstr);
236 }
237 is_exit=(o->type==MI_T_RESULT_RECORD && o->tclass==MI_CL_EXIT);
238 /* Add to the list of responses. */
239 if (add)
240 {
241 if (h->last)
242 h->last->next=o;
243 else
244 h->po=o;
245 h->last=o;
246 }
247 else
248 mi_free_output(o);
249 /* Exit RR means gdb exited, we won't get a new prompt ;-) */
250 if (is_exit)
251 return 1;
252 }
253
254 return 0;
255}
256
257mi_output *mi_retire_response(mi_h *h)
258{
259 mi_output *ret=h->po;
260 h->po=h->last=NULL;
261 return ret;
262}
263
264mi_output *mi_get_response_blk(mi_h *h)
265{
266 int r;
267 /* Sometimes gdb dies. */
268 if (!mi_check_running(h))
269 {
270 h->died=1;
271 mi_error=MI_GDB_DIED;
272 return NULL;
273 }
274 do
275 {
276 /*
277 That's a must. If we just keep trying to read and failing things
278 become really sloooowwww. Instead we try and if it fails we wait
279 until something is available.
280 TODO: Implement something with the time out, a callback to ask the
281 application is we have to wait or not could be a good thing.
282 */
283 fd_set set;
284 struct timeval timeout;
285 int ret;
286
287 r=mi_get_response(h);
288 if (r)
289 return mi_retire_response(h);
290
291 FD_ZERO(&set);
292 FD_SET(h->from_gdb[0],&set);
293 timeout.tv_sec=h->time_out;
294 timeout.tv_usec=0;
295 ret=TEMP_FAILURE_RETRY(select(FD_SETSIZE,&set,NULL,NULL,&timeout));
296 if (!ret)
297 {
298 if (!mi_check_running(h))
299 {
300 h->died=1;
301 mi_error=MI_GDB_DIED;
302 return NULL;
303 }
304 if (h->time_out_cb)
305 ret=h->time_out_cb(h->time_out_cb_data);
306 if (!ret)
307 {
308 mi_error=MI_GDB_TIME_OUT;
309 return NULL;
310 }
311 }
312 }
313 while (!r);
314
315 return NULL;
316}
317
318void mi_send_commands(mi_h *h, const char *file)
319{
320 FILE *f;
321 char b[PATH_MAX];
322
323 //printf("File: %s\n",file);
324 if (!file)
325 return;
326 f=fopen(file,"rt");
327 if (!f)
328 return;
329 while (!feof(f))
330 {
331 if (fgets(b,PATH_MAX,f))
332 {
333 //printf("Send: %s\n",b);
334 mi_send (h, "%s", b);
335 mi_res_simple_done(h);
336 }
337 }
338 fclose(f);
339}
340
341void mi_send_target_commands(mi_h *h)
342{
343 mi_send_commands(h,gdb_conn);
344}
345
346/**[txh]********************************************************************
347
348 Description:
349 Connect to a local copy of gdb. Note that the mi_h structure is something
350similar to a "FILE *" for stdio.
351
352 Return: A new mi_h structure or NULL on error.
353
354***************************************************************************/
355
356mi_h *mi_connect_local()
357{
358 mi_h *h;
359 const char *gdb=mi_get_gdb_exe();
360
361 /* Start without error. */
362 mi_error=MI_OK;
363 /* Verify we have a GDB binary. */
364 if (access(gdb,X_OK))
365 {
366 mi_error=MI_MISSING_GDB;
367 return NULL;
368 }
369 /* Alloc the handle structure. */
370 h=mi_alloc_h();
371 if (!h)
372 return h;
373 h->time_out=MI_DEFAULT_TIME_OUT;
374 /* Create the pipes to connect with the child. */
375 if (pipe(h->to_gdb) || pipe(h->from_gdb))
376 {
377 mi_error=MI_PIPE_CREATE;
378 mi_free_h(&h);
379 return NULL;
380 }
381 mi_set_nonblk(h->to_gdb[1]);
382 mi_set_nonblk(h->from_gdb[0]);
383 /* Associate streams to the file handles. */
384 h->to=fdopen(h->to_gdb[1],"w");
385 h->from=fdopen(h->from_gdb[0],"r");
386 if (!h->to || !h->from)
387 {
388 mi_error=MI_PIPE_CREATE;
389 mi_free_h(&h);
390 return NULL;
391 }
392 /* Create the child. */
393 h->pid=fork();
394 if (h->pid==0)
395 {/* We are the child. */
396 char *argv[5];
397 /* Connect stdin/out to the pipes. */
398 dup2(h->to_gdb[0],STDIN_FILENO);
399 dup2(h->from_gdb[1],STDOUT_FILENO);
400 /* Pass the control to gdb. */
401 argv[0]=(char *)gdb; /* Is that OK? */
402 argv[1]="--interpreter=mi";
403 argv[2]="--quiet";
404 argv[3]=disable_psym_search_workaround ? 0 : "--readnow";
405 argv[4]=0;
406 execvp(argv[0],argv);
407 /* We get here only if exec failed. */
408 _exit(127);
409 }
410 /* We are the parent. */
411 if (h->pid==-1)
412 {/* Fork failed. */
413 mi_error=MI_FORK;
414 mi_free_h(&h);
415 return NULL;
416 }
417 if (!mi_check_running(h))
418 {
419 mi_error=MI_DEBUGGER_RUN;
420 mi_free_h(&h);
421 return NULL;
422 }
423 /* Wait for the prompt. */
424 mi_get_response_blk(h);
425 /* Send the start-up commands */
426 mi_send_commands(h,gdb_start);
427
428 return h;
429}
430
431/**[txh]********************************************************************
432
433 Description:
434 Close connection. You should ask gdb to quit first gmi_gdb_exit.
435
436***************************************************************************/
437
438void mi_disconnect(mi_h *h)
439{
440 mi_free_h(&h);
441 free(mi_error_from_gdb);
442 mi_error_from_gdb=NULL;
443}
444
445void mi_set_console_cb(mi_h *h, stream_cb cb, void *data)
446{
447 h->console=cb;
448 h->console_data=data;
449}
450
451void mi_set_target_cb(mi_h *h, stream_cb cb, void *data)
452{
453 h->target=cb;
454 h->target_data=data;
455}
456
457void mi_set_log_cb(mi_h *h, stream_cb cb, void *data)
458{
459 h->log=cb;
460 h->log_data=data;
461}
462
463stream_cb mi_get_console_cb(mi_h *h, void **data)
464{
465 if (data)
466 *data=h->console_data;
467 return h->console;
468}
469
470stream_cb mi_get_target_cb(mi_h *h, void **data)
471{
472 if (data)
473 *data=h->target_data;
474 return h->target;
475}
476
477stream_cb mi_get_log_cb(mi_h *h, void **data)
478{
479 if (data)
480 *data=h->log_data;
481 return h->log;
482}
483
484void mi_set_async_cb(mi_h *h, async_cb cb, void *data)
485{
486 h->async=cb;
487 h->async_data=data;
488}
489
490async_cb mi_get_async_cb(mi_h *h, void **data)
491{
492 if (data)
493 *data=h->async_data;
494 return h->async;
495}
496
497void mi_set_to_gdb_cb(mi_h *h, stream_cb cb, void *data)
498{
499 h->to_gdb_echo=cb;
500 h->to_gdb_echo_data=data;
501}
502
503void mi_set_from_gdb_cb(mi_h *h, stream_cb cb, void *data)
504{
505 h->from_gdb_echo=cb;
506 h->from_gdb_echo_data=data;
507}
508
509stream_cb mi_get_to_gdb_cb(mi_h *h, void **data)
510{
511 if (data)
512 *data=h->to_gdb_echo_data;
513 return h->to_gdb_echo;
514}
515
516stream_cb mi_get_from_gdb_cb(mi_h *h, void **data)
517{
518 if (data)
519 *data=h->from_gdb_echo_data;
520 return h->from_gdb_echo;
521}
522
523void mi_set_time_out_cb(mi_h *h, tm_cb cb, void *data)
524{
525 h->time_out_cb=cb;
526 h->time_out_cb_data=data;
527}
528
529tm_cb mi_get_time_out_cb(mi_h *h, void **data)
530{
531 if (data)
532 *data=h->time_out_cb_data;
533 return h->time_out_cb;
534}
535
536void mi_set_time_out(mi_h *h, int to)
537{
538 h->time_out=to;
539}
540
541int mi_get_time_out(mi_h *h)
542{
543 return h->time_out;
544}
545
546int mi_send(mi_h *h, const char *format, ...)
547{
548 int ret;
549 char *str;
550 va_list argptr;
551
552 if (h->died)
553 return 0;
554
555 va_start(argptr,format);
556 ret=vasprintf(&str,format,argptr);
557 va_end(argptr);
558 if (-1 != ret)
559 {
560 fputs(str,h->to);
561 fflush(h->to);
562 if (h->to_gdb_echo)
563 h->to_gdb_echo(str,h->to_gdb_echo_data);
564 free(str);
565 }
566 else
567 {
568 abort ();
569 }
570
571 return ret;
572}
573
574void mi_clean_up_globals()
575{
576 free(gdb_exe);
577 gdb_exe=NULL;
578 free(xterm_exe);
579 xterm_exe=NULL;
580 free(gdb_start);
581 gdb_start=NULL;
582 free(gdb_conn);
583 gdb_conn=NULL;
584 free(main_func);
585 main_func=NULL;
586}
587
588void mi_register_exit()
589{
590 static int registered=0;
591 if (!registered)
592 {
593 registered=1;
594 atexit(mi_clean_up_globals);
595 }
596}
597
598void mi_set_gdb_exe(const char *name)
599{
600 free(gdb_exe);
601 gdb_exe=name ? strdup(name) : NULL;
602 mi_register_exit();
603}
604
605void mi_set_gdb_start(const char *name)
606{
607 free(gdb_start);
608 gdb_start=name ? strdup(name) : NULL;
609 mi_register_exit();
610}
611
612void mi_set_gdb_conn(const char *name)
613{
614 free(gdb_conn);
615 gdb_conn=name ? strdup(name) : NULL;
616 mi_register_exit();
617}
618
619static
620char *mi_search_in_path(const char *file)
621{
622 char *path, *pt, *r;
623 char test[PATH_MAX];
624 struct stat st;
625
626 path=getenv("PATH");
627 if (!path)
628 return NULL;
629 pt=strdup(path);
630 r=strtok(pt,PATH_SEPARATOR_STR);
631 while (r)
632 {
633 strcpy(test,r);
634 strcat(test,"/");
635 strcat(test,file);
636 if (stat(test,&st)==0 && S_ISREG(st.st_mode))
637 {
638 free(pt);
639 return strdup(test);
640 }
641 r=strtok(NULL,PATH_SEPARATOR_STR);
642 }
643 free(pt);
644 return NULL;
645}
646
647const char *mi_get_gdb_exe()
648{
649 if (!gdb_exe)
650 {/* Look for gdb in path */
651 gdb_exe=mi_search_in_path("gdb");
652 if (!gdb_exe)
653 return "/usr/bin/gdb";
654 }
655 return gdb_exe;
656}
657
658const char *mi_get_gdb_start()
659{
660 return gdb_start;
661}
662
663const char *mi_get_gdb_conn()
664{
665 return gdb_conn;
666}
667
668void mi_set_xterm_exe(const char *name)
669{
670 free(xterm_exe);
671 xterm_exe=name ? strdup(name) : NULL;
672 mi_register_exit();
673}
674
675const char *mi_get_xterm_exe()
676{
677 if (!xterm_exe)
678 {/* Look for xterm in path */
679 xterm_exe=mi_search_in_path("xterm");
680 if (!xterm_exe)
681 return "/usr/bin/X11/xterm";
682 }
683 return xterm_exe;
684}
685
686void mi_set_main_func(const char *name)
687{
688 free(main_func);
689 main_func=name ? strdup(name) : NULL;
690 mi_register_exit();
691}
692
693const char *mi_get_main_func()
694{
695 if (main_func)
696 return main_func;
697 return "main";
698}
699
700/**[txh]********************************************************************
701
702 Description:
703 Opens a new xterm to be used by the child process to debug.
704
705 Return: A new mi_aux_term structure, you can use gmi_end_aux_term to
706release it.
707
708***************************************************************************/
709
710mi_aux_term *gmi_start_xterm()
711{
712 char nsh[14]="/tmp/shXXXXXX";
713 char ntt[14]="/tmp/ttXXXXXX";
714 const char *xterm;
715 struct stat st;
716 int hsh, htt=-1;
717 mi_aux_term *res=NULL;
718 FILE *f;
719 pid_t pid;
720 char buf[PATH_MAX];
721
722 /* Verify we have an X terminal. */
723 xterm=mi_get_xterm_exe();
724 if (access(xterm,X_OK))
725 {
726 mi_error=MI_MISSING_XTERM;
727 return NULL;
728 }
729
730 /* Create 2 temporals. */
731 hsh=mkstemp(nsh);
732 if (hsh==-1)
733 {
734 mi_error=MI_CREATE_TEMPORAL;
735 return NULL;
736 }
737 htt=mkstemp(ntt);
738 if (htt==-1)
739 {
740 close(hsh);
741 unlink(nsh);
742 mi_error=MI_CREATE_TEMPORAL;
743 return NULL;
744 }
745 close(htt);
746 /* Create the script. */
747 f=fdopen(hsh,"w");
748 if (!f)
749 {
750 close(hsh);
751 unlink(nsh);
752 unlink(ntt);
753 mi_error=MI_CREATE_TEMPORAL;
754 return NULL;
755 }
756 fprintf(f,"#!/bin/sh\n");
757 fprintf(f,"tty > %s\n",ntt);
758 fprintf(f,"rm %s\n",nsh);
759 fprintf(f,"sleep 365d\n");
760 fclose(f);
761 /* Spawn xterm. */
762 /* Create the child. */
763 pid=fork();
764 if (pid==0)
765 {/* We are the child. */
766 char *argv[5];
767 /* Pass the control to gdb. */
768 argv[0]=(char *)mi_get_xterm_exe(); /* Is that ok? */
769 argv[1]="-e";
770 argv[2]="/bin/sh";
771 argv[3]=nsh;
772 argv[4]=0;
773 execvp(argv[0],argv);
774 /* We get here only if exec failed. */
775 unlink(nsh);
776 unlink(ntt);
777 _exit(127);
778 }
779 /* We are the parent. */
780 if (pid==-1)
781 {/* Fork failed. */
782 unlink(nsh);
783 unlink(ntt);
784 mi_error=MI_FORK;
785 return NULL;
786 }
787 /* Wait until the shell is deleted. */
788 while (stat(nsh,&st)==0)
789 usleep(1000);
790 /* Try to read the tty name. */
791 f=fopen(ntt,"rt");
792 if (f)
793 {
794 if (fgets(buf,PATH_MAX,f))
795 {
796 char *s; /* Strip the \n. */
797 for (s=buf; *s && *s!='\n'; s++);
798 *s=0;
799 res=(mi_aux_term *)malloc(sizeof(mi_aux_term));
800 if (res)
801 {
802 res->pid=pid;
803 res->tty=strdup(buf);
804 }
805 }
806 fclose(f);
807 }
808 unlink(ntt);
809 return res;
810}
811
812void mi_free_aux_term(mi_aux_term *t)
813{
814 if (!t)
815 return;
816 free(t->tty);
817 free(t);
818}
819
820/**[txh]********************************************************************
821
822 Description:
823 Closes the auxiliar terminal and releases the allocated memory.
824
825***************************************************************************/
826
827void gmi_end_aux_term(mi_aux_term *t)
828{
829 if (!t)
830 return;
831 if (t->pid!=-1 && mi_check_running_pid(t->pid))
832 mi_kill_child(t->pid);
833 mi_free_aux_term(t);
834}
835
836/**[txh]********************************************************************
837
838 Description:
839 Forces the MI version. Currently the library can't detect it so you must
840force it manually. GDB 5.x implemented MI v1 and 6.x v2.
841
842***************************************************************************/
843
844void mi_force_version(mi_h *h, unsigned vMajor, unsigned vMiddle,
845 unsigned vMinor)
846{
847 h->version=MI_VERSION2U(vMajor,vMiddle,vMinor);
848}
849
850/**[txh]********************************************************************
851
852 Description:
853 Dis/Enables the workaround for a bug in gdb.
854
855***************************************************************************/
856
857void mi_set_workaround(unsigned wa, int enable)
858{
859 switch (wa)
860 {
861 case MI_PSYM_SEARCH:
862 disable_psym_search_workaround=enable ? 0 : 1;
863 break;
864 }
865}
866
867/**[txh]********************************************************************
868
869 Description:
870 Finds if the workaround for a bug in gdb is enabled.
871
872 Return: !=0 if enabled.
873
874***************************************************************************/
875
876int mi_get_workaround(unsigned wa)
877{
878 switch (wa)
879 {
880 case MI_PSYM_SEARCH:
881 return disable_psym_search_workaround==0;
882 }
883 return 0;
884}
885
diff --git a/pathologist/src/mi/gdbmi_data_man.c b/pathologist/src/mi/gdbmi_data_man.c
new file mode 100644
index 0000000..06e1377
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_data_man.c
@@ -0,0 +1,243 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: Data manipulation.
7 Comments:
8 GDB/MI commands for the "Data manipulation" section.
9
10 @<p>
11
12@<pre>
13gdb command: Implemented?
14
15-data-disassemble Yes
16-data-evaluate-expression Yes
17-data-list-changed-registers No
18-data-list-register-names Yes
19-data-list-register-values No
20-data-read-memory No
21-display-delete N.A. (delete display)
22-display-disable N.A. (disable display)
23-display-enable N.A. (enable display)
24-display-insert N.A. (display)
25-display-list N.A. (info display)
26-environment-cd No
27-environment-directory Yes, MI v1 implementation
28-environment-path No
29@</pre>
30
31Notes: @<p>
32
331) -display* aren't implemented. You can use CLI command display, but the
34results are sent to the console. So it looks like the best is to manually
35use -data-evaluate-expression to emulate it. @<p>
36
372) GDB bug mi/1770: Affects gdb<=6.2, when you ask for the names of the
38registers you get it plus the name of the "pseudo-registers", but if you
39try to get the value of a pseudo-register you get an error saying the
40register number is invalid. I reported to gdb-patches@sources.redhat.com
41on 2004/08/25 and as I didn't get any answer I filled a bug report on
422004/09/02. The patch to fix this annoying bug is:
43
44Index: gdb/mi/mi-main.c
45===================================================================
46RCS file: /cvs/src/src/gdb/mi/mi-main.c,v
47retrieving revision 1.64
48diff -u -r1.64 mi-main.c
49--- gdb/mi/mi-main.c 3 Aug 2004 00:57:27 -0000 1.64
50+++ gdb/mi/mi-main.c 25 Aug 2004 14:12:50 -0000
51@@ -423,7 +423,7 @@
52 case, some entries of REGISTER_NAME will change depending upon
53 the particular processor being debugged.
54
55- numregs = NUM_REGS;
56+ numregs = NUM_REGS + NUM_PSEUDO_REGS;
57
58 if (argc == 0)
59 {
60----
61
62Note I had to remove an end of comment in the patch to include it here.
63This bug forced me to create another set of functions. The only way is to
64first get the values and then the names.
65Fixed by Changelog entry:
66
672004-09-12 Salvador E. Tropea <set@users.sf.net>
68 Andrew Cagney <cagney@gnu.org>
69
70 * mi/mi-main.c (mi_cmd_data_list_changed_registers)
71 (mi_cmd_data_list_register_values)
72 (mi_cmd_data_write_register_values): Include the PSEUDO_REGS in
73 the register number computation.
74
75***************************************************************************/
76
77#include "gdbmi.h"
78
79/* Low level versions. */
80
81void mi_data_evaluate_expression(mi_h *h, const char *expression)
82{
83 mi_send(h,"-data-evaluate-expression \"%s\"\n",expression);
84}
85
86void mi_dir(mi_h *h, const char *path)
87{
88 if (h->version>=MI_VERSION2U(2,0,0))
89 {// MI v2
90 if (path)
91 mi_send(h,"-environment-directory \"%s\"\n",path);
92 else
93 mi_send(h,"-environment-directory -r\n");
94 }
95 else
96 {
97 mi_send(h,"-environment-directory %s\n",path ? path : "");
98 }
99}
100
101void mi_data_read_memory_hx(mi_h *h, const char *exp, unsigned ws,
102 unsigned c, int convAddr)
103{
104 if (convAddr)
105 mi_send(h,"-data-read-memory \"&%s\" x %d 1 %d\n",exp,ws,c);
106 else
107 mi_send(h,"-data-read-memory \"%s\" x %d 1 %d\n",exp,ws,c);
108}
109
110void mi_data_disassemble_se(mi_h *h, const char *start, const char *end,
111 int mode)
112{
113 mi_send(h,"-data-disassemble -s \"%s\" -e \"%s\" -- %d\n",start,end,mode);
114}
115
116void mi_data_disassemble_fl(mi_h *h, const char *file, int line, int lines,
117 int mode)
118{
119 mi_send(h,"-data-disassemble -f \"%s\" -l %d -n %d -- %d\n",file,line,lines,
120 mode);
121}
122
123void mi_data_list_register_names(mi_h *h)
124{
125 mi_send(h,"-data-list-register-names\n");
126}
127
128void mi_data_list_register_names_l(mi_h *h, mi_chg_reg *l)
129{
130 mi_send(h,"-data-list-register-names ");
131 while (l)
132 {
133 mi_send(h,"%d ",l->reg);
134 l=l->next;
135 }
136 mi_send(h,"\n");
137}
138
139void mi_data_list_changed_registers(mi_h *h)
140{
141 mi_send(h,"-data-list-changed-registers\n");
142}
143
144void mi_data_list_register_values(mi_h *h, enum mi_gvar_fmt fmt, mi_chg_reg *l)
145{
146 mi_send(h,"-data-list-register-values %c ",mi_format_enum_to_char(fmt));
147 while (l)
148 {
149 mi_send(h,"%d ",l->reg);
150 l=l->next;
151 }
152 mi_send(h,"\n");
153}
154
155/* High level versions. */
156
157/**[txh]********************************************************************
158
159 Description:
160 Evaluate an expression. Returns a parsed tree.
161
162 Command: -data-evaluate-expression
163 Return: The resulting value (as plain text) or NULL on error.
164
165***************************************************************************/
166
167char *gmi_data_evaluate_expression(mi_h *h, const char *expression)
168{
169 mi_data_evaluate_expression(h,expression);
170 return mi_res_value(h);
171}
172
173/**[txh]********************************************************************
174
175 Description:
176 Path for sources. You must use it to indicate where are the sources for
177the program to debug. Only the MI v1 implementation is available.
178
179 Command: -environment-directory
180 Return: !=0 OK
181
182***************************************************************************/
183
184int gmi_dir(mi_h *h, const char *path)
185{
186 mi_dir(h,path);
187 return mi_res_simple_done(h);
188}
189
190int gmi_read_memory(mi_h *h, const char *exp, unsigned size,
191 unsigned char *dest, int *na, int convAddr,
192 unsigned long *addr)
193{
194 mi_data_read_memory_hx(h,exp,1,size,convAddr);
195 return mi_get_read_memory(h,dest,1,na,addr);
196}
197
198mi_asm_insns *gmi_data_disassemble_se(mi_h *h, const char *start,
199 const char *end, int mode)
200{
201 mi_data_disassemble_se(h,start,end,mode);
202 return mi_get_asm_insns(h);
203}
204
205mi_asm_insns *gmi_data_disassemble_fl(mi_h *h, const char *file, int line,
206 int lines, int mode)
207{
208 mi_data_disassemble_fl(h,file,line,lines,mode);
209 return mi_get_asm_insns(h);
210}
211
212// Affected by gdb bug mi/1770
213mi_chg_reg *gmi_data_list_register_names(mi_h *h, int *how_many)
214{
215 mi_data_list_register_names(h);
216 return mi_get_list_registers(h,how_many);
217}
218
219int gmi_data_list_register_names_l(mi_h *h, mi_chg_reg *l)
220{
221 mi_data_list_register_names_l(h,l);
222 return mi_get_list_registers_l(h,l);
223}
224
225mi_chg_reg *gmi_data_list_changed_registers(mi_h *h)
226{
227 mi_error=MI_OK;
228 mi_data_list_changed_registers(h);
229 return mi_get_list_changed_regs(h);
230}
231
232int gmi_data_list_register_values(mi_h *h, enum mi_gvar_fmt fmt, mi_chg_reg *l)
233{
234 mi_data_list_register_values(h,fmt,l);
235 return mi_get_reg_values(h,l);
236}
237
238mi_chg_reg *gmi_data_list_all_register_values(mi_h *h, enum mi_gvar_fmt fmt, int *how_many)
239{
240 mi_data_list_register_values(h,fmt,NULL);
241 return mi_get_reg_values_l(h,how_many);
242}
243
diff --git a/pathologist/src/mi/gdbmi_error.c b/pathologist/src/mi/gdbmi_error.c
new file mode 100644
index 0000000..abb20f6
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_error.c
@@ -0,0 +1,38 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: Error.
7 Comment:
8 Translates error numbers into messages.
9
10***************************************************************************/
11
12#include "gdbmi.h"
13
14static
15const char *error_strs[]=
16{
17 "Ok",
18 "Out of memory",
19 "Pipe creation",
20 "Fork failed",
21 "GDB not running",
22 "Parser failed",
23 "Unknown asyn response",
24 "Unknown result response",
25 "Error from gdb",
26 "Time out in gdb response",
27 "GDB suddenly died",
28 "Can't execute X terminal",
29 "Failed to create temporal",
30 "Can't execute the debugger"
31};
32
33const char *mi_get_error_str()
34{
35 if (mi_error<0 || mi_error>MI_LAST_ERROR)
36 return "Unknown";
37 return error_strs[mi_error];
38}
diff --git a/pathologist/src/mi/gdbmi_get_free_pty.c b/pathologist/src/mi/gdbmi_get_free_pty.c
new file mode 100644
index 0000000..4274c78
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_get_free_pty.c
@@ -0,0 +1,132 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: pseudo terminal
7 Comments:
8 Helper to find a free pseudo terminal. Use this if you need to manage
9 input *and* output to the target process. If you just need output then
10 define a handler for target output stream records (assuming that this
11 is working for your particular version of gdb).
12 Usage:
13
14 mi_pty *pty = gmi_look_for_free_pty();
15 if (pty) gmi_target_terminal(mih, pty->slave);
16 ...
17 * reading from pty->master will get stdout from target *
18 * writing to pty->master will send to target stdin *
19
20 Note: Contributed by Greg Watson (gwatson lanl gov)
21
22***************************************************************************/
23
24#define _GNU_SOURCE
25#include <string.h>
26#include <stdio.h>
27#include <unistd.h>
28#include <fcntl.h>
29#include <sys/ioctl.h>
30
31#include "gdbmi.h"
32
33/**[txh]********************************************************************
34
35 Description:
36 Look for a free and usable pseudo terminal. Low level, use
37 gmi_look_for_free_pty().
38
39 Return: A file descriptor connected to the master pty and the name of the slave device, or <0 on error.
40
41***************************************************************************/
42
43#ifdef __APPLE__
44
45#include <util.h>
46
47int mi_look_for_free_pty(int *master, char **slave)
48{
49 int fdmaster;
50 int fdslave;
51 static char name[BUFSIZ];
52
53 if (openpty(&fdmaster,&fdslave,name,NULL,NULL)<0)
54 return -1;
55
56 (void)close(fdslave); /* this will be reopened by gdb */
57 *master=fdmaster;
58 *slave =name;
59
60 return 0;
61}
62
63#elif defined(__linux__)
64
65int mi_look_for_free_pty(int *master, char **slave)
66{
67 if ((*master=open("/dev/ptmx",O_RDWR))<0)
68 return -1;
69 if (grantpt(*master)<0 || unlockpt(*master)<0)
70 return -1;
71 *slave = ptsname(*master);
72
73 return 0;
74}
75
76#else /* undefined o/s */
77
78int mi_look_for_free_pty(int *master, char **slave)
79{
80 return -1;
81}
82#endif
83
84/**[txh]********************************************************************
85
86 Description:
87 Look for a free and usable pseudo terminal to be used by the child.
88
89 Return: A new mi_pty structure, you can use gmi_end_pty to
90release it.
91
92***************************************************************************/
93
94mi_pty *gmi_look_for_free_pty()
95{
96 int master;
97 char *slave;
98 int pty=mi_look_for_free_pty(&master,&slave);
99 mi_pty *res;
100
101 if (pty<0)
102 return NULL;
103 res=(mi_pty *)malloc(sizeof(mi_pty));
104 if (!res)
105 return NULL;
106 res->slave=strdup(slave);
107 res->master=master;
108 return res;
109}
110
111void mi_free_pty(mi_pty *p)
112{
113 if (!p)
114 return;
115 free(p->slave);
116 free(p);
117}
118
119/**[txh]********************************************************************
120
121 Description:
122 Closes the pseudo termial master and releases the allocated memory.
123
124***************************************************************************/
125
126void gmi_end_pty(mi_pty *p)
127{
128 if (!p)
129 return;
130 close(p->master);
131 mi_free_pty(p);
132}
diff --git a/pathologist/src/mi/gdbmi_get_free_vt.c b/pathologist/src/mi/gdbmi_get_free_vt.c
new file mode 100644
index 0000000..25e5e75
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_get_free_vt.c
@@ -0,0 +1,156 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: Linux VT.
7 Comments:
8 Helper to find a free VT. That's 100% Linux specific.@p
9 The code comes from "lconsole.c" from Allegro project and was originally
10created by Marek Habersack and then modified by George Foot. I addapted it
11to my needs and changed license from giftware to GPL.@p
12
13***************************************************************************/
14
15#define _GNU_SOURCE
16#include <string.h>
17#include <stdio.h>
18#include <unistd.h>
19#include <fcntl.h>
20#include <sys/ioctl.h>
21#ifdef __APPLE__
22#include <util.h>
23#endif /* __APPLE__ */
24
25#include "gdbmi.h"
26
27#if !defined(__linux__)
28
29int mi_look_for_free_vt()
30{
31 return -1;
32}
33
34mi_aux_term *gmi_look_for_free_vt()
35{
36 return NULL;
37}
38
39#else
40
41#include <linux/vt.h>
42
43/**[txh]********************************************************************
44
45 Description:
46 Look for a free and usable Linux VT. Low level, use
47@x{gmi_look_for_free_vt}.
48
49 Return: The VT number or <0 on error.
50
51***************************************************************************/
52
53int mi_look_for_free_vt()
54{/* Code from Allegro. */
55 int tty, console_fd, fd;
56 unsigned short mask;
57 char tty_name[16];
58 struct vt_stat vts;
59
60 /* Now we need to find a VT we can use. It must be readable and
61 * writable by us, if we're not setuid root. VT_OPENQRY itself
62 * isn't too useful because it'll only ever come up with one
63 * suggestion, with no guarrantee that we actually have access
64 * to it.
65 *
66 * At some stage I think this is a candidate for config
67 * file overriding, but for now we'll stat the first N consoles
68 * to see which ones we can write to (hopefully at least one!),
69 * so that we can use that one to do ioctls. We used to use
70 * /dev/console for that purpose but it looks like it's not
71 * always writable by enough people.
72 *
73 * Having found and opened a writable device, we query the state
74 * of the first sixteen (fifteen really) consoles, and try
75 * opening each unused one in turn.
76 */
77
78 console_fd=open("/dev/console",O_WRONLY);
79 if (console_fd<0)
80 {
81 int n;
82 /* Try some ttys instead... */
83 for (n=1; n<=24; n++)
84 {
85 snprintf(tty_name,sizeof(tty_name),"/dev/tty%d",n);
86 console_fd=open(tty_name,O_WRONLY);
87 if (console_fd>=0)
88 break;
89 }
90 if (n>24)
91 return -1;
92 }
93
94 /* Get the state of the console -- in particular, the free VT field */
95 if (ioctl(console_fd,VT_GETSTATE,&vts)) {
96 close(console_fd);
97 return -2;
98 }
99 close(console_fd);
100
101 /* We attempt to set our euid to 0; if we were run with euid 0 to
102 * start with, we'll be able to do this now. Otherwise, we'll just
103 * ignore the error returned since it might not be a problem if the
104 * ttys we look at are owned by the user running the program. */
105 seteuid(0);
106
107 /* tty0 is not really a console, so start counting at 2. */
108 fd=-1;
109 for (tty=1, mask=2; mask; tty++, mask<<=1)
110 if (!(vts.v_state & mask))
111 {
112 snprintf(tty_name,sizeof(tty_name),"/dev/tty%d",tty);
113 fd=open(tty_name,O_RDWR);
114 if (fd!=-1)
115 {
116 close(fd);
117 break;
118 }
119 }
120
121 seteuid(getuid());
122
123 if (!mask)
124 return -3;
125
126 return tty;
127}
128
129/**[txh]********************************************************************
130
131 Description:
132 Look for a free and usable Linux VT to be used by the child.
133
134 Return: A new mi_aux_term structure, you can use @x{gmi_end_aux_term} to
135release it.
136
137***************************************************************************/
138
139mi_aux_term *gmi_look_for_free_vt()
140{
141 int ret;
142 int vt=mi_look_for_free_vt();
143 mi_aux_term *res;
144
145 if (vt<0)
146 return NULL;
147 res=(mi_aux_term *)malloc(sizeof(mi_aux_term));
148 if (!res)
149 return NULL;
150 res->pid=-1;
151 ret = asprintf(&res->tty,"/dev/tty%d",vt);
152 return res;
153}
154
155#endif
156
diff --git a/pathologist/src/mi/gdbmi_misc.c b/pathologist/src/mi/gdbmi_misc.c
new file mode 100644
index 0000000..51088a7
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_misc.c
@@ -0,0 +1,118 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: Miscellaneous commands.
7 Comments:
8 GDB/MI commands for the "Miscellaneous Commands" section. @<p>
9
10@<pre>
11gdb command: Implemented?
12
13-gdb-exit Yes
14-gdb-set Yes
15-gdb-show Yes
16-gdb-version Yes
17@</pre>
18
19GDB Bug workaround for "-gdb-show architecture": gdb 6.1 and olders doesn't
20report it in "value", but they give the output of "show architecture". In
216.4 we observed that not even a clue is reported. So now we always use
22"show architecture".
23
24***************************************************************************/
25
26#include <string.h>
27#include "gdbmi.h"
28
29/* Low level versions. */
30
31void mi_gdb_exit(mi_h *h)
32{
33 mi_send(h,"-gdb-exit\n");
34}
35
36void mi_gdb_version(mi_h *h)
37{
38 mi_send(h,"-gdb-version\n");
39}
40
41void mi_gdb_set(mi_h *h, const char *var, const char *val)
42{
43 mi_send(h,"-gdb-set %s %s\n",var,val);
44}
45
46void mi_gdb_show(mi_h *h, const char *var)
47{
48 if (strcmp(var,"architecture")==0)
49 mi_send(h,"show %s\n",var);
50 else
51 mi_send(h,"-gdb-show %s\n",var);
52}
53
54/* High level versions. */
55
56/**[txh]********************************************************************
57
58 Description:
59 Exit gdb killing the child is it is running.
60
61 Command: -gdb-exit
62
63***************************************************************************/
64
65void gmi_gdb_exit(mi_h *h)
66{
67 mi_gdb_exit(h);
68 mi_res_simple_exit(h);
69}
70
71/**[txh]********************************************************************
72
73 Description:
74 Send the version to the console.
75
76 Command: -gdb-version
77 Return: !=0 OK
78
79***************************************************************************/
80
81int gmi_gdb_version(mi_h *h)
82{
83 mi_gdb_version(h);
84 return mi_res_simple_done(h);
85}
86
87/**[txh]********************************************************************
88
89 Description:
90 Set a gdb variable.
91
92 Command: -gdb-set
93 Return: !=0 OK
94
95***************************************************************************/
96
97int gmi_gdb_set(mi_h *h, const char *var, const char *val)
98{
99 mi_gdb_set(h,var,val);
100 return mi_res_simple_done(h);
101}
102
103/**[txh]********************************************************************
104
105 Description:
106 Get a gdb variable.
107
108 Command: -gdb-show
109 Return: The current value of the variable or NULL on error.
110
111***************************************************************************/
112
113char *gmi_gdb_show(mi_h *h, const char *var)
114{
115 mi_gdb_show(h,var);
116 return mi_res_value(h);
117}
118
diff --git a/pathologist/src/mi/gdbmi_parse.c b/pathologist/src/mi/gdbmi_parse.c
new file mode 100644
index 0000000..d30bd22
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_parse.c
@@ -0,0 +1,1927 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004-2007 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: Parser.
7 Comments:
8 Parses the output of gdb. It basically converts the text from gdb into a
9tree (could be a complex one) that we can easily interpret using C code.
10
11***************************************************************************/
12
13#include <ctype.h>
14#include <string.h>
15#include <assert.h>
16#include "gdbmi.h"
17
18mi_results *mi_get_result(const char *str, const char **end);
19int mi_get_value(mi_results *r, const char *str, const char **end);
20
21
22/* GDB BUG!!!! I got:
23^error,msg="Problem parsing arguments: data-evaluate-expression ""1+2"""
24Afects gdb 2002-04-01-cvs and 6.1.1 for sure.
25That's an heuristical workaround.
26*/
27static inline
28int EndOfStr(const char *s)
29{
30 if (*s=='"')
31 {
32 s++;
33 return !*s || *s==',' || *s==']' || *s=='}';
34 }
35 return 0;
36}
37
38int mi_get_cstring_r(mi_results *r, const char *str, const char **end)
39{
40 const char *s;
41 char *d;
42 int len;
43
44 if (*str!='"')
45 {
46 mi_error=MI_PARSER;
47 return 0;
48 }
49 str++;
50 /* Meassure. */
51 for (s=str, len=0; *s && !EndOfStr(s); s++)
52 {
53 if (!*s) {
54 mi_error = MI_PARSER;
55 return 0;
56 }
57 if (*s=='\\')
58 s++;
59 len++;
60 }
61 /* Copy. */
62 r->type=t_const;
63 d=r->v.cstr=mi_malloc(len+1);
64 if (!r->v.cstr)
65 return 0;
66 for (s=str; *s && !EndOfStr(s); s++, d++)
67 {
68 if (*s=='\\')
69 {
70 s++;
71 switch (*s)
72 {
73 case 'n':
74 *d='\n';
75 break;
76 case 't':
77 *d='\t';
78 break;
79 default:
80 *d=*s;
81 }
82 }
83 else
84 *d=*s;
85 }
86 *d=0;
87 if (end)
88 *end=s+1;
89
90 return 1;
91}
92
93/* TODO: What's a valid variable name?
94 I'll assume a-zA-Z0-9_- */
95//inline
96int mi_is_var_name_char(char c)
97{
98 return isalnum(c) || c=='-' || c=='_';
99}
100
101char *mi_get_var_name(const char *str, const char **end)
102{
103 const char *s;
104 char *r;
105 int l;
106 /* Meassure. */
107 for (s=str; *s && mi_is_var_name_char(*s); s++);
108 if (*s!='=')
109 {
110 mi_error=MI_PARSER;
111 return NULL;
112 }
113 /* Allocate. */
114 l=s-str;
115 r=mi_malloc(l+1);
116 /* Copy. */
117 if (NULL != r) {
118 memcpy(r,str,l);
119 r[l]=0;
120 }
121 if (end)
122 *end=s+1;
123 return r;
124}
125
126
127int mi_get_list_res(mi_results *r, const char *str, const char **end, char closeC)
128{
129 mi_results *last_r, *rs;
130
131 last_r=NULL;
132 do
133 {
134 rs=mi_get_result(str,&str);
135 if (last_r)
136 last_r->next=rs;
137 else
138 r->v.rs=rs;
139 last_r=rs;
140 if (*str==closeC)
141 {
142 *end=str+1;
143 return 1;
144 }
145 if (*str!=',')
146 break;
147 str++;
148 }
149 while (1);
150
151 mi_error=MI_PARSER;
152 return 0;
153}
154
155#ifdef __APPLE__
156int mi_get_tuple_val(mi_results *r, const char *str, const char **end)
157{
158 mi_results *last_r, *rs;
159
160 last_r=NULL;
161 do
162 {
163 rs=mi_alloc_results();
164 if (!rs || !mi_get_value(rs,str,&str))
165 {
166 mi_free_results(rs);
167 return 0;
168 }
169 /* Note that rs->var is NULL, that indicates that's just a value and not
170 a result. */
171 if (last_r)
172 last_r->next=rs;
173 else
174 r->v.rs=rs;
175 last_r=rs;
176 if (*str=='}')
177 {
178 *end=str+1;
179 return 1;
180 }
181 if (*str!=',')
182 break;
183 str++;
184 }
185 while (1);
186
187 mi_error=MI_PARSER;
188 return 0;
189}
190#endif /* __APPLE__ */
191
192int mi_get_tuple(mi_results *r, const char *str, const char **end)
193{
194 if (*str!='{')
195 {
196 mi_error=MI_PARSER;
197 return 0;
198 }
199 r->type=t_tuple;
200 str++;
201 if (*str=='}')
202 {/* Special case: empty tuple */
203 *end=str+1;
204 return 1;
205 }
206 #ifdef __APPLE__
207 if (mi_is_var_name_char(*str))
208 return mi_get_list_res(r,str,end,'}');
209 return mi_get_tuple_val(r,str,end);
210 #else /* __APPLE__ */
211 return mi_get_list_res(r,str,end,'}');
212 #endif /* __APPLE__ */
213}
214
215int mi_get_list_val(mi_results *r, const char *str, const char **end)
216{
217 mi_results *last_r, *rs;
218
219 last_r=NULL;
220 do
221 {
222 rs=mi_alloc_results();
223 if (!rs || !mi_get_value(rs,str,&str))
224 {
225 mi_free_results(rs);
226 return 0;
227 }
228 /* Note that rs->var is NULL, that indicates that's just a value and not
229 a result. */
230 if (last_r)
231 last_r->next=rs;
232 else
233 r->v.rs=rs;
234 last_r=rs;
235 if (*str==']')
236 {
237 *end=str+1;
238 return 1;
239 }
240 if (*str!=',')
241 break;
242 str++;
243 }
244 while (1);
245
246 mi_error=MI_PARSER;
247 return 0;
248}
249
250int mi_get_list(mi_results *r, const char *str, const char **end)
251{
252 if (*str!='[')
253 {
254 mi_error=MI_PARSER;
255 return 0;
256 }
257 r->type=t_list;
258 str++;
259 if (*str==']')
260 {/* Special case: empty list */
261 *end=str+1;
262 return 1;
263 }
264 /* Comment: I think they could choose () for values. Is confusing in this way. */
265 if (mi_is_var_name_char(*str))
266 return mi_get_list_res(r,str,end,']');
267 return mi_get_list_val(r,str,end);
268}
269
270int mi_get_value(mi_results *r, const char *str, const char **end)
271{
272 switch (str[0])
273 {
274 case '"':
275 return mi_get_cstring_r(r,str,end);
276 case '{':
277 return mi_get_tuple(r,str,end);
278 case '[':
279 return mi_get_list(r,str,end);
280 }
281 mi_error=MI_PARSER;
282 return 0;
283}
284
285mi_results *mi_get_result(const char *str, const char **end)
286{
287 char *var;
288 mi_results *r;
289
290 var=mi_get_var_name(str,&str);
291 if (!var)
292 return NULL;
293
294 r=mi_alloc_results();
295 if (!r)
296 {
297 free(var);
298 return NULL;
299 }
300 r->var=var;
301
302 if (!mi_get_value(r,str,end))
303 {
304 mi_free_results(r);
305 return NULL;
306 }
307
308 return r;
309}
310
311mi_output *mi_get_results_alone(mi_output *r,const char *str)
312{
313 mi_results *last_r, *rs;
314
315 /* * results */
316 last_r=NULL;
317 do
318 {
319 if (!*str)
320 return r;
321 if (*str!=',')
322 {
323 mi_error=MI_PARSER;
324 break;
325 }
326 str++;
327 rs=mi_get_result(str,&str);
328 if (!rs)
329 break;
330 if (!last_r)
331 r->c=rs;
332 else
333 last_r->next=rs;
334 last_r=rs;
335 }
336 while (1);
337 mi_free_output(r);
338 return NULL;
339}
340
341mi_output *mi_parse_result_record(mi_output *r,const char *str)
342{
343 r->type=MI_T_RESULT_RECORD;
344
345 /* Solve the result-class. */
346 if (strncmp(str,"done",4)==0)
347 {
348 str+=4;
349 r->tclass=MI_CL_DONE;
350 }
351 else if (strncmp(str,"running",7)==0)
352 {
353 str+=7;
354 r->tclass=MI_CL_RUNNING;
355 }
356 else if (strncmp(str,"connected",9)==0)
357 {
358 str+=9;
359 r->tclass=MI_CL_CONNECTED;
360 }
361 else if (strncmp(str,"error",5)==0)
362 {
363 str+=5;
364 r->tclass=MI_CL_ERROR;
365 }
366 else if (strncmp(str,"exit",4)==0)
367 {
368 str+=4;
369 r->tclass=MI_CL_EXIT;
370 }
371 else
372 {
373 mi_error=MI_UNKNOWN_RESULT;
374 return NULL;
375 }
376
377 return mi_get_results_alone(r,str);
378}
379
380mi_output *mi_parse_asyn(mi_output *r,const char *str)
381{
382 r->type=MI_T_OUT_OF_BAND;
383 r->stype=MI_ST_ASYNC;
384 /* async-class. */
385 if (strncmp(str,"stopped",7)==0)
386 {
387 r->tclass=MI_CL_STOPPED;
388 str+=7;
389 return mi_get_results_alone(r,str);
390 }
391 if (strncmp(str,"download",8)==0)
392 {
393 r->tclass=MI_CL_DOWNLOAD;
394 str+=8;
395 return mi_get_results_alone(r,str);
396 }
397 mi_error=MI_UNKNOWN_ASYNC;
398 mi_free_output(r);
399 return NULL;
400}
401
402mi_output *mi_parse_exec_asyn(mi_output *r,const char *str)
403{
404 r->sstype=MI_SST_EXEC;
405 return mi_parse_asyn(r,str);
406}
407
408mi_output *mi_parse_status_asyn(mi_output *r,const char *str)
409{
410 r->sstype=MI_SST_STATUS;
411 return mi_parse_asyn(r,str);
412}
413
414mi_output *mi_parse_notify_asyn(mi_output *r,const char *str)
415{
416 r->sstype=MI_SST_NOTIFY;
417 return mi_parse_asyn(r,str);
418}
419
420mi_output *mi_console(mi_output *r,const char *str)
421{
422 r->type=MI_T_OUT_OF_BAND;
423 r->stype=MI_ST_STREAM;
424 r->c=mi_alloc_results();
425 if (!r->c || !mi_get_cstring_r(r->c,str,NULL))
426 {
427 mi_free_output(r);
428 return NULL;
429 }
430 return r;
431}
432
433mi_output *mi_console_stream(mi_output *r,const char *str)
434{
435 r->sstype=MI_SST_CONSOLE;
436 return mi_console(r,str);
437}
438
439mi_output *mi_target_stream(mi_output *r,const char *str)
440{
441 r->sstype=MI_SST_TARGET;
442 return mi_console(r,str);
443}
444
445mi_output *mi_log_stream(mi_output *r,const char *str)
446{
447 r->sstype=MI_SST_LOG;
448 return mi_console(r,str);
449}
450
451mi_output *mi_parse_gdb_output(const char *str)
452{
453 char type=str[0];
454
455 mi_output *r=mi_alloc_output();
456 if (!r)
457 {
458 mi_error=MI_OUT_OF_MEMORY;
459 return NULL;
460 }
461 str++;
462 switch (type)
463 {
464 case '^':
465 return mi_parse_result_record(r,str);
466 case '*':
467 return mi_parse_exec_asyn(r,str);
468 case '+':
469 return mi_parse_status_asyn(r,str);
470 case '=':
471 return mi_parse_notify_asyn(r,str);
472 case '~':
473 return mi_console_stream(r,str);
474 case '@':
475 return mi_target_stream(r,str);
476 case '&':
477 return mi_log_stream(r,str);
478 }
479 mi_error=MI_PARSER;
480 return NULL;
481}
482
483mi_output *mi_get_rrecord(mi_output *r)
484{
485 if (!r)
486 return NULL;
487 while (r)
488 {
489 if (r->type==MI_T_RESULT_RECORD)
490 return r;
491 r=r->next;
492 }
493 return r;
494}
495
496mi_results *mi_get_var_r(mi_results *r, const char *var)
497{
498 while (r)
499 {
500 if (strcmp(r->var,var)==0)
501 return r;
502 r=r->next;
503 }
504 return NULL;
505}
506
507mi_results *mi_get_var(mi_output *res, const char *var)
508{
509 if (!res)
510 return NULL;
511 return mi_get_var_r(res->c,var);
512}
513
514int mi_get_async_stop_reason(mi_output *r, char **reason)
515{
516 int found_stopped=0;
517
518 *reason=NULL;
519 while (r)
520 {
521 if (r->type==MI_T_RESULT_RECORD && r->tclass==MI_CL_ERROR)
522 {
523 if (r->c->type==t_const)
524 *reason=r->c->v.cstr;
525 return 0;
526 }
527 if (r->type==MI_T_OUT_OF_BAND && r->stype==MI_ST_ASYNC &&
528 r->sstype==MI_SST_EXEC && r->tclass==MI_CL_STOPPED)
529 {
530 mi_results *p=r->c;
531 found_stopped=1;
532 while (p)
533 {
534 if (strcmp(p->var,"reason")==0)
535 {
536 *reason=p->v.cstr;
537 return 1;
538 }
539 p=p->next;
540 }
541 }
542 r=r->next;
543 }
544 if (*reason==NULL && found_stopped)
545 {
546 *reason=strdup("unknown (temp bkpt?)");
547 return 1;
548 }
549 return 0;
550}
551
552mi_frames *mi_get_async_frame(mi_output *r)
553{
554 while (r)
555 {
556 if (r->type==MI_T_OUT_OF_BAND && r->stype==MI_ST_ASYNC &&
557 r->sstype==MI_SST_EXEC && r->tclass==MI_CL_STOPPED)
558 {
559 mi_results *p=r->c;
560 while (p)
561 {
562 if (strcmp(p->var,"frame")==0)
563 return mi_parse_frame(p->v.rs);
564 p=p->next;
565 }
566 }
567 r=r->next;
568 }
569 return NULL;
570}
571
572int mi_res_simple(mi_h *h, int tclass, int accert_ret)
573{
574 mi_output *r, *res;
575 int ret=0;
576
577 r=mi_get_response_blk(h);
578 res=mi_get_rrecord(r);
579
580 if (res)
581 ret=res->tclass==tclass;
582 mi_free_output(r);
583
584 return ret;
585}
586
587
588int mi_res_simple_done(mi_h *h)
589{
590 return mi_res_simple(h,MI_CL_DONE,0);
591}
592
593int mi_res_simple_exit(mi_h *h)
594{
595 return mi_res_simple(h,MI_CL_EXIT,1);
596}
597
598int mi_res_simple_running(mi_h *h)
599{
600 return mi_res_simple(h,MI_CL_RUNNING,0);
601}
602
603int mi_res_simple_connected(mi_h *h)
604{
605 return mi_res_simple(h,MI_CL_CONNECTED,0);
606}
607
608mi_results *mi_res_var(mi_h *h, const char *var, int tclass)
609{
610 mi_output *r, *res;
611 mi_results *the_var=NULL;
612
613 r=mi_get_response_blk(h);
614 /* All the code that follows is "NULL" tolerant. */
615 /* Look for the result-record. */
616 res=mi_get_rrecord(r);
617 /* Look for the desired var. */
618 if (res && res->tclass==tclass)
619 the_var=mi_get_var(res,var);
620 /* Release all but the one we want. */
621 mi_free_output_but(r,NULL,the_var);
622 return the_var;
623}
624
625mi_results *mi_res_done_var(mi_h *h, const char *var)
626{
627 return mi_res_var(h,var,MI_CL_DONE);
628}
629
630mi_frames *mi_parse_frame(mi_results *c)
631{
632 mi_frames *res=mi_alloc_frames();
633 char *end;
634
635 if (res)
636 {
637 while (c)
638 {
639 if (c->type==t_const)
640 {
641 if (strcmp(c->var,"level")==0)
642 res->level=atoi(c->v.cstr);
643 else if (strcmp(c->var,"addr")==0)
644 res->addr=(void *)strtoul(c->v.cstr,&end,0);
645 else if (strcmp(c->var,"func")==0)
646 {
647 res->func=c->v.cstr;
648 c->v.cstr=NULL;
649 }
650 else if (strcmp(c->var,"file")==0)
651 {
652 res->file=c->v.cstr;
653 c->v.cstr=NULL;
654 }
655 else if (strcmp(c->var,"from")==0)
656 {
657 res->from=c->v.cstr;
658 c->v.cstr=NULL;
659 }
660 else if (strcmp(c->var,"line")==0)
661 res->line=atoi(c->v.cstr);
662 }
663 else if (c->type==t_list && strcmp(c->var,"args")==0)
664 {
665 res->args=c->v.rs;
666 c->v.rs=NULL;
667 }
668 c=c->next;
669 }
670 }
671 return res;
672}
673
674mi_frames *mi_res_frame(mi_h *h)
675{
676 mi_results *r=mi_res_done_var(h,"frame");
677 mi_frames *f=NULL;
678
679 if (r && r->type==t_tuple)
680 f=mi_parse_frame(r->v.rs);
681 mi_free_results(r);
682 return f;
683}
684
685mi_frames *mi_res_frames_array(mi_h *h, const char *var)
686{
687 mi_results *r=mi_res_done_var(h,var), *c;
688 mi_frames *res=NULL, *nframe, *last=NULL;
689
690 if (!r)
691 return NULL;
692#ifdef __APPLE__
693 if (r->type!=t_list && r->type!=t_tuple)
694#else
695 if (r->type!=t_list)
696#endif
697 {
698 mi_free_results(r);
699 return NULL;
700 }
701 c=r->v.rs;
702 while (c)
703 {
704 if (strcmp(c->var,"frame")==0 && c->type==t_tuple)
705 {
706 nframe=mi_parse_frame(c->v.rs);
707 if (nframe)
708 {
709 if (!last)
710 res=nframe;
711 else
712 last->next=nframe;
713 last=nframe;
714 }
715 }
716 c=c->next;
717 }
718 mi_free_results(r);
719 return res;
720}
721
722mi_frames *mi_res_frames_list(mi_h *h)
723{
724 mi_output *r, *res;
725 mi_frames *ret=NULL, *nframe, *last=NULL;
726 mi_results *c;
727
728 r=mi_get_response_blk(h);
729 res=mi_get_rrecord(r);
730 if (res && res->tclass==MI_CL_DONE)
731 {
732 c=res->c;
733 while (c)
734 {
735 if (strcmp(c->var,"frame")==0 && c->type==t_tuple)
736 {
737 nframe=mi_parse_frame(c->v.rs);
738 if (nframe)
739 {
740 if (!last)
741 ret=nframe;
742 else
743 last->next=nframe;
744 last=nframe;
745 }
746 }
747 c=c->next;
748 }
749 }
750 mi_free_output(r);
751 return ret;
752}
753
754int mi_get_thread_ids(mi_output *res, int **list)
755{
756 mi_results *vids, *lids;
757 int ids=-1, i;
758
759 *list=NULL;
760 vids=mi_get_var(res,"number-of-threads");
761 lids=mi_get_var(res,"thread-ids");
762 if (vids && vids->type==t_const &&
763 lids && lids->type==t_tuple)
764 {
765 ids=atoi(vids->v.cstr);
766 if (ids)
767 {
768 int *lst;
769 lst=(int *)mi_calloc(ids,sizeof(int));
770 if (lst)
771 {
772 lids=lids->v.rs;
773 i=0;
774 while (lids)
775 {
776 if (strcmp(lids->var,"thread-id")==0 && lids->type==t_const)
777 lst[i++]=atoi(lids->v.cstr);
778 lids=lids->next;
779 }
780 *list=lst;
781 }
782 else
783 ids=-1;
784 }
785 }
786 return ids;
787}
788
789int mi_res_thread_ids(mi_h *h, int **list)
790{
791 mi_output *r, *res;
792 int ids=-1;
793
794 r=mi_get_response_blk(h);
795 res=mi_get_rrecord(r);
796 if (res && res->tclass==MI_CL_DONE)
797 ids=mi_get_thread_ids(res,list);
798 mi_free_output(r);
799 return ids;
800}
801
802enum mi_gvar_lang mi_lang_str_to_enum(const char *lang)
803{
804 enum mi_gvar_lang lg=lg_unknown;
805
806 if (strcmp(lang,"C")==0)
807 lg=lg_c;
808 else if (strcmp(lang,"C++")==0)
809 lg=lg_cpp;
810 else if (strcmp(lang,"Java")==0)
811 lg=lg_java;
812
813 return lg;
814}
815
816const char *mi_lang_enum_to_str(enum mi_gvar_lang lang)
817{
818 const char *lg;
819
820 switch (lang)
821 {
822 case lg_c:
823 lg="C";
824 break;
825 case lg_cpp:
826 lg="C++";
827 break;
828 case lg_java:
829 lg="Java";
830 break;
831 /*case lg_unknown:*/
832 default:
833 lg="unknown";
834 break;
835 }
836 return lg;
837}
838
839enum mi_gvar_fmt mi_format_str_to_enum(const char *format)
840{
841 enum mi_gvar_fmt fmt=fm_natural;
842
843 if (strcmp(format,"binary")==0)
844 fmt=fm_binary;
845 else if (strcmp(format,"decimal")==0)
846 fmt=fm_decimal;
847 else if (strcmp(format,"hexadecimal")==0)
848 fmt=fm_hexadecimal;
849 else if (strcmp(format,"octal")==0)
850 fmt=fm_octal;
851
852 return fmt;
853}
854
855const char *mi_format_enum_to_str(enum mi_gvar_fmt format)
856{
857 const char *fmt;
858
859 switch (format)
860 {
861 case fm_natural:
862 fmt="natural";
863 break;
864 case fm_binary:
865 fmt="binary";
866 break;
867 case fm_decimal:
868 fmt="decimal";
869 break;
870 case fm_hexadecimal:
871 fmt="hexadecimal";
872 break;
873 case fm_octal:
874 fmt="octal";
875 break;
876 case fm_raw:
877 fmt="raw";
878 break;
879 default:
880 fmt="unknown";
881 }
882 return fmt;
883}
884
885char mi_format_enum_to_char(enum mi_gvar_fmt format)
886{
887 char fmt;
888
889 switch (format)
890 {
891 case fm_natural:
892 fmt='N';
893 break;
894 case fm_binary:
895 fmt='t';
896 break;
897 case fm_decimal:
898 fmt='d';
899 break;
900 case fm_hexadecimal:
901 fmt='x';
902 break;
903 case fm_octal:
904 fmt='o';
905 break;
906 case fm_raw:
907 fmt='r';
908 break;
909 default:
910 fmt=' ';
911 }
912 return fmt;
913}
914
915mi_gvar *mi_get_gvar(mi_output *o, mi_gvar *cur, const char *expression)
916{
917 mi_results *r;
918 mi_gvar *res=cur ? cur : mi_alloc_gvar();
919 int l;
920
921 if (!res)
922 return res;
923 r=o->c;
924 if (expression)
925 res->exp=strdup(expression);
926 while (r)
927 {
928 if (r->type==t_const)
929 {
930 if (strcmp(r->var,"name")==0)
931 {
932 free(res->name);
933 res->name=r->v.cstr;
934 r->v.cstr=NULL;
935 }
936 else if (strcmp(r->var,"numchild")==0)
937 {
938 res->numchild=atoi(r->v.cstr);
939 }
940 else if (strcmp(r->var,"type")==0)
941 {
942 free(res->type);
943 res->type=r->v.cstr;
944 r->v.cstr=NULL;
945 l=strlen(res->type);
946 if (l && res->type[l-1]=='*')
947 res->ispointer=1;
948 }
949 else if (strcmp(r->var,"lang")==0)
950 {
951 res->lang=mi_lang_str_to_enum(r->v.cstr);
952 }
953 else if (strcmp(r->var,"exp")==0)
954 {
955 free(res->exp);
956 res->exp=r->v.cstr;
957 r->v.cstr=NULL;
958 }
959 else if (strcmp(r->var,"format")==0)
960 {
961 res->format=mi_format_str_to_enum(r->v.cstr);
962 }
963 else if (strcmp(r->var,"attr")==0)
964 { /* Note: gdb 6.1.1 have only this: */
965 if (strcmp(r->v.cstr,"editable")==0)
966 res->attr=MI_ATTR_EDITABLE;
967 else /* noneditable */
968 res->attr=MI_ATTR_NONEDITABLE;
969 }
970 }
971 r=r->next;
972 }
973 return res;
974}
975
976mi_gvar *mi_res_gvar(mi_h *h, mi_gvar *cur, const char *expression)
977{
978 mi_output *r, *res;
979 mi_gvar *gvar=NULL;
980
981 r=mi_get_response_blk(h);
982 res=mi_get_rrecord(r);
983 if (res && res->tclass==MI_CL_DONE)
984 gvar=mi_get_gvar(res,cur,expression);
985 mi_free_output(r);
986 return gvar;
987}
988
989mi_gvar_chg *mi_get_gvar_chg(mi_results *r)
990{
991 mi_gvar_chg *n;
992
993 if (r->type!=t_const)
994 return NULL;
995 n=mi_alloc_gvar_chg();
996 if (n)
997 {
998 while (r)
999 {
1000 if (r->type==t_const)
1001 {
1002 if (strcmp(r->var,"name")==0)
1003 {
1004 n->name=r->v.cstr;
1005 r->v.cstr=NULL;
1006 }
1007 else if (strcmp(r->var,"in_scope")==0)
1008 {
1009 n->in_scope=strcmp(r->v.cstr,"true")==0;
1010 }
1011 else if (strcmp(r->var,"new_type")==0)
1012 {
1013 n->new_type=r->v.cstr;
1014 r->v.cstr=NULL;
1015 }
1016 else if (strcmp(r->var,"new_num_children")==0)
1017 {
1018 n->new_num_children=atoi(r->v.cstr);
1019 }
1020 // type_changed="false" is the default
1021 }
1022 r=r->next;
1023 }
1024 }
1025 return n;
1026}
1027
1028int mi_res_changelist(mi_h *h, mi_gvar_chg **changed)
1029{
1030 mi_gvar_chg *last, *n;
1031 mi_results *res=mi_res_done_var(h,"changelist"), *r;
1032 int count=0;
1033
1034 *changed=NULL;
1035 if (!res)
1036 return 0;
1037 last=NULL;
1038 count=1;
1039 n=NULL;
1040 r=res->v.rs;
1041
1042 if (res->type==t_list)
1043 {// MI v2 a list of tuples
1044 while (r)
1045 {
1046 if (r->type==t_tuple)
1047 {
1048 n=mi_get_gvar_chg(r->v.rs);
1049 if (n)
1050 {
1051 if (last)
1052 last->next=n;
1053 else
1054 *changed=n;
1055 last=n;
1056 count++;
1057 }
1058 }
1059 r=r->next;
1060 }
1061 }
1062 else if (res->type==t_tuple)
1063 {// MI v1 a tuple with all together *8-P
1064 while (r)
1065 {
1066 if (r->type==t_const) /* Just in case. */
1067 {/* Get one var. */
1068 if (strcmp(r->var,"name")==0)
1069 {
1070 if (n)
1071 {/* Add to the list*/
1072 if (last)
1073 last->next=n;
1074 else
1075 *changed=n;
1076 last=n;
1077 count++;
1078 }
1079 n=mi_alloc_gvar_chg();
1080 if (!n)
1081 {
1082 mi_free_gvar_chg(*changed);
1083 return 0;
1084 }
1085 n->name=r->v.cstr;
1086 r->v.cstr=NULL;
1087 }
1088 else if ((NULL != n) && (strcmp(r->var,"in_scope")==0))
1089 {
1090 n->in_scope=strcmp(r->v.cstr,"true")==0;
1091 }
1092 else if ((NULL != n) && (strcmp(r->var,"new_type")==0))
1093 {
1094 n->new_type=r->v.cstr;
1095 r->v.cstr=NULL;
1096 }
1097 else if ((NULL != n) && (strcmp(r->var,"new_num_children")==0))
1098 {
1099 n->new_num_children=atoi(r->v.cstr);
1100 }
1101 // type_changed="false" is the default
1102 }
1103 r=r->next;
1104 }
1105 if (n)
1106 {/* Add to the list*/
1107 if (last)
1108 last->next=n;
1109 else
1110 *changed=n;
1111 last=n;
1112 count++;
1113 }
1114 }
1115 mi_free_results(res);
1116
1117 return count;
1118}
1119
1120int mi_get_children(mi_results *ch, mi_gvar *v)
1121{
1122 mi_gvar *cur=NULL, *aux;
1123 int i=0, count=v->numchild, l;
1124
1125 while (ch)
1126 {
1127 if (strcmp(ch->var,"child")==0 && ch->type==t_tuple && i<count)
1128 {
1129 mi_results *r=ch->v.rs;
1130 aux=mi_alloc_gvar();
1131 if (!aux)
1132 return 0;
1133 if (!v->child)
1134 v->child=aux;
1135 else if (NULL != cur)
1136 cur->next=aux;
1137 cur=aux;
1138 cur->parent=v;
1139 cur->depth=v->depth+1;
1140
1141 while (r)
1142 {
1143 if (r->type==t_const)
1144 {
1145 if (strcmp(r->var,"name")==0)
1146 {
1147 cur->name=r->v.cstr;
1148 r->v.cstr=NULL;
1149 }
1150 else if (strcmp(r->var,"exp")==0)
1151 {
1152 cur->exp=r->v.cstr;
1153 r->v.cstr=NULL;
1154 }
1155 else if (strcmp(r->var,"type")==0)
1156 {
1157 cur->type=r->v.cstr;
1158 r->v.cstr=NULL;
1159 l=strlen(cur->type);
1160 if (l && cur->type[l-1]=='*')
1161 cur->ispointer=1;
1162 }
1163 else if (strcmp(r->var,"value")==0)
1164 {
1165 cur->value=r->v.cstr;
1166 r->v.cstr=NULL;
1167 }
1168 else if (strcmp(r->var,"numchild")==0)
1169 {
1170 cur->numchild=atoi(r->v.cstr);
1171 }
1172 }
1173 r=r->next;
1174 }
1175 i++;
1176 }
1177 ch=ch->next;
1178 }
1179 v->vischild=i;
1180 v->opened=1;
1181 return i==v->numchild;
1182}
1183
1184int mi_res_children(mi_h *h, mi_gvar *v)
1185{
1186 mi_output *r, *res;
1187 int ok=0;
1188
1189 r=mi_get_response_blk(h);
1190 res=mi_get_rrecord(r);
1191 if (res && res->tclass==MI_CL_DONE)
1192 {
1193 mi_results *num=mi_get_var(res,"numchild");
1194 if (num && num->type==t_const)
1195 {
1196 v->numchild=atoi(num->v.cstr);
1197 if (v->child)
1198 {
1199 mi_free_gvar(v->child);
1200 v->child=NULL;
1201 }
1202 if (v->numchild)
1203 {
1204 mi_results *ch =mi_get_var(res,"children");
1205 if (ch && ch->type!=t_const) /* MI v1 tuple, MI v2 list */
1206 ok=mi_get_children(ch->v.rs,v);
1207 }
1208 else
1209 ok=1;
1210 }
1211 }
1212 mi_free_output(r);
1213 return ok;
1214}
1215
1216mi_bkpt *mi_get_bkpt(mi_results *p)
1217{
1218 mi_bkpt *res;
1219 char *end;
1220
1221 res=mi_alloc_bkpt();
1222 if (!res)
1223 return NULL;
1224 while (p)
1225 {
1226 if (p->type==t_const && p->var)
1227 {
1228 if (strcmp(p->var,"number")==0)
1229 res->number=atoi(p->v.cstr);
1230 else if (strcmp(p->var,"type")==0)
1231 {
1232 if (strcmp(p->v.cstr,"breakpoint")==0)
1233 res->type=t_breakpoint;
1234 else
1235 res->type=t_unknown;
1236 }
1237 else if (strcmp(p->var,"disp")==0)
1238 {
1239 if (strcmp(p->v.cstr,"keep")==0)
1240 res->disp=d_keep;
1241 else if (strcmp(p->v.cstr,"del")==0)
1242 res->disp=d_del;
1243 else
1244 res->disp=d_unknown;
1245 }
1246 else if (strcmp(p->var,"enabled")==0)
1247 res->enabled=p->v.cstr[0]=='y';
1248 else if (strcmp(p->var,"addr")==0)
1249 res->addr=(void *)strtoul(p->v.cstr,&end,0);
1250 else if (strcmp(p->var,"func")==0)
1251 {
1252 res->func=p->v.cstr;
1253 p->v.cstr=NULL;
1254 }
1255 else if (strcmp(p->var,"file")==0)
1256 {
1257 res->file=p->v.cstr;
1258 p->v.cstr=NULL;
1259 }
1260 else if (strcmp(p->var,"line")==0)
1261 res->line=atoi(p->v.cstr);
1262 else if (strcmp(p->var,"times")==0)
1263 res->times=atoi(p->v.cstr);
1264 else if (strcmp(p->var,"ignore")==0)
1265 res->ignore=atoi(p->v.cstr);
1266 else if (strcmp(p->var,"cond")==0)
1267 {
1268 res->cond=p->v.cstr;
1269 p->v.cstr=NULL;
1270 }
1271 }
1272 p=p->next;
1273 }
1274 return res;
1275}
1276
1277mi_bkpt *mi_res_bkpt(mi_h *h)
1278{
1279 mi_results *r=mi_res_done_var(h,"bkpt");
1280 mi_bkpt *b=NULL;
1281
1282 if (r && r->type==t_tuple)
1283 b=mi_get_bkpt(r->v.rs);
1284 mi_free_results(r);
1285 return b;
1286}
1287
1288mi_wp *mi_get_wp(mi_results *p, enum mi_wp_mode m)
1289{
1290 mi_wp *res=mi_alloc_wp();
1291
1292 if (res)
1293 {
1294 res->mode=m;
1295 while (p)
1296 {
1297 if (p->type==t_const && p->var)
1298 {
1299 if (strcmp(p->var,"number")==0)
1300 {
1301 res->number=atoi(p->v.cstr);
1302 res->enabled=1;
1303 }
1304 else if (strcmp(p->var,"exp")==0)
1305 {
1306 res->exp=p->v.cstr;
1307 p->v.cstr=NULL;
1308 }
1309 }
1310 p=p->next;
1311 }
1312 }
1313 return res;
1314}
1315
1316mi_wp *mi_parse_wp_res(mi_output *r)
1317{
1318 mi_results *p;
1319 enum mi_wp_mode m=wm_unknown;
1320
1321 /* The info is in a result wpt=... */
1322 p=r->c;
1323 while (p)
1324 {
1325 if (p->var)
1326 {
1327 if (strcmp(p->var,"wpt")==0)
1328 m=wm_write;
1329 else if (strcmp(p->var,"hw-rwpt")==0)
1330 m=wm_read;
1331 else if (strcmp(p->var,"hw-awpt")==0)
1332 m=wm_rw;
1333 if (m!=wm_unknown)
1334 break;
1335 }
1336 p=p->next;
1337 }
1338 if (!p || p->type!=t_tuple)
1339 return NULL;
1340 /* Scan the values inside it. */
1341 return mi_get_wp(p->v.rs,m);
1342}
1343
1344mi_wp *mi_res_wp(mi_h *h)
1345{
1346 mi_output *r, *res;
1347 mi_wp *ret=NULL;
1348
1349 r=mi_get_response_blk(h);
1350 res=mi_get_rrecord(r);
1351
1352 if (res)
1353 ret=mi_parse_wp_res(res);
1354
1355 mi_free_output(r);
1356 return ret;
1357}
1358
1359char *mi_res_value(mi_h *h)
1360{
1361 mi_results *r=mi_res_done_var(h,"value");
1362 char *s=NULL;
1363
1364 if (r && r->type==t_const)
1365 {
1366 s=r->v.cstr;
1367 r->v.rs=NULL;
1368 }
1369 mi_free_results(r);
1370 return s;
1371}
1372
1373mi_output *mi_get_stop_record(mi_output *r)
1374{
1375 while (r)
1376 {
1377 if (r->type==MI_T_OUT_OF_BAND && r->stype==MI_ST_ASYNC &&
1378 r->sstype==MI_SST_EXEC && r->tclass==MI_CL_STOPPED)
1379 return r;
1380 r=r->next;
1381 }
1382 return r;
1383}
1384
1385static
1386char *reason_names[]=
1387{
1388 "breakpoint-hit",
1389 "watchpoint-trigger",
1390 "read-watchpoint-trigger",
1391 "access-watchpoint-trigger",
1392 "watchpoint-scope",
1393 "function-finished",
1394 "location-reached",
1395 "end-stepping-range",
1396 "exited-signalled",
1397 "exited",
1398 "exited-normally",
1399 "signal-received"
1400};
1401
1402static
1403enum mi_stop_reason reason_values[]=
1404{
1405 sr_bkpt_hit,
1406 sr_wp_trigger, sr_read_wp_trigger, sr_access_wp_trigger, sr_wp_scope,
1407 sr_function_finished, sr_location_reached, sr_end_stepping_range,
1408 sr_exited_signalled, sr_exited, sr_exited_normally,
1409 sr_signal_received
1410};
1411
1412static
1413char *reason_expl[]=
1414{
1415 "Hit a breakpoint",
1416 "Write watchpoint",
1417 "Read watchpoint",
1418 "Access watchpoint",
1419 "Watchpoint out of scope",
1420 "Function finished",
1421 "Location reached",
1422 "End of stepping",
1423 "Exited signalled",
1424 "Exited with error",
1425 "Exited normally",
1426 "Signal received"
1427};
1428
1429enum mi_stop_reason mi_reason_str_to_enum(const char *s)
1430{
1431 int i;
1432
1433 for (i=0; i<sizeof(reason_names)/sizeof(char *); i++)
1434 if (strcmp(reason_names[i],s)==0)
1435 return reason_values[i];
1436 return sr_unknown;
1437}
1438
1439const char *mi_reason_enum_to_str(enum mi_stop_reason r)
1440{
1441 int i;
1442
1443 if (r==sr_unknown)
1444 return "Unknown (temp bkp?)";
1445 for (i=0; i<sizeof(reason_values)/sizeof(char *); i++)
1446 if (reason_values[i]==r)
1447 return reason_expl[i];
1448 return NULL;
1449}
1450
1451mi_stop *mi_get_stopped(mi_results *r)
1452{
1453 mi_stop *res=mi_alloc_stop();
1454
1455 if (res)
1456 {
1457 while (r)
1458 {
1459 if (r->type==t_const)
1460 {
1461 if (strcmp(r->var,"reason")==0)
1462 res->reason=mi_reason_str_to_enum(r->v.cstr);
1463 else if (!res->have_thread_id && strcmp(r->var,"thread-id")==0)
1464 {
1465 res->have_thread_id=1;
1466 res->thread_id=atoi(r->v.cstr);
1467 }
1468 else if (!res->have_bkptno && strcmp(r->var,"bkptno")==0)
1469 {
1470 res->have_bkptno=1;
1471 res->bkptno=atoi(r->v.cstr);
1472 }
1473 else if (!res->have_bkptno && strcmp(r->var,"wpnum")==0)
1474 {
1475 res->have_wpno=1;
1476 res->wpno=atoi(r->v.cstr);
1477 }
1478 else if (strcmp(r->var,"gdb-result-var")==0)
1479 {
1480 res->gdb_result_var=r->v.cstr;
1481 r->v.cstr=NULL;
1482 }
1483 else if (strcmp(r->var,"return-value")==0)
1484 {
1485 res->return_value=r->v.cstr;
1486 r->v.cstr=NULL;
1487 }
1488 else if (strcmp(r->var,"signal-name")==0)
1489 {
1490 res->signal_name=r->v.cstr;
1491 r->v.cstr=NULL;
1492 }
1493 else if (strcmp(r->var,"signal-meaning")==0)
1494 {
1495 res->signal_meaning=r->v.cstr;
1496 r->v.cstr=NULL;
1497 }
1498 else if (!res->have_exit_code && strcmp(r->var,"exit-code")==0)
1499 {
1500 res->have_exit_code=1;
1501 res->exit_code=atoi(r->v.cstr);
1502 }
1503 }
1504 else // tuple or list
1505 {
1506 if (strcmp(r->var,"frame")==0)
1507 res->frame=mi_parse_frame(r->v.rs);
1508 else if (!res->wp && strcmp(r->var,"wpt")==0)
1509 res->wp=mi_get_wp(r->v.rs,wm_write);
1510 else if (!res->wp && strcmp(r->var,"hw-rwpt")==0)
1511 res->wp=mi_get_wp(r->v.rs,wm_read);
1512 else if (!res->wp && strcmp(r->var,"hw-awpt")==0)
1513 res->wp=mi_get_wp(r->v.rs,wm_rw);
1514 else if (!(res->wp_old || res->wp_val) && strcmp(r->var,"value")==0)
1515 {
1516 mi_results *p=r->v.rs;
1517 while (p)
1518 {
1519 if (strcmp(p->var,"value")==0 || strcmp(p->var,"new")==0)
1520 {
1521 res->wp_val=p->v.cstr;
1522 p->v.cstr=NULL;
1523 }
1524 else if (strcmp(p->var,"old")==0)
1525 {
1526 res->wp_old=p->v.cstr;
1527 p->v.cstr=NULL;
1528 }
1529 p=p->next;
1530 }
1531 }
1532 }
1533 r=r->next;
1534 }
1535 }
1536 return res;
1537}
1538
1539mi_stop *mi_res_stop(mi_h *h)
1540{
1541 mi_output *o=mi_retire_response(h);
1542 mi_stop *stop=NULL;
1543
1544 if (o)
1545 {
1546 mi_output *sr=mi_get_stop_record(o);
1547 if (sr)
1548 stop=mi_get_stopped(sr->c);
1549 }
1550 mi_free_output(o);
1551
1552 return stop;
1553}
1554
1555int mi_get_read_memory(mi_h *h, unsigned char *dest, unsigned ws, int *na,
1556 unsigned long *addr)
1557{
1558 char *end;
1559 mi_results *res=mi_res_done_var(h,"memory"), *r;
1560 int ok=0;
1561
1562 *na=0;
1563 r=res;
1564 if (r && r->type==t_list && ws==1)
1565 {
1566 r=r->v.rs;
1567 if (r->type!=t_tuple)
1568 {
1569 mi_free_results(res);
1570 return 0;
1571 }
1572 r=r->v.rs;
1573 while (r)
1574 {
1575 if (r->type==t_list && strcmp(r->var,"data")==0)
1576 {
1577 mi_results *data=r->v.rs;
1578 ok++;
1579 if (data && data->type==t_const &&
1580 strcmp(data->v.cstr,"N/A")==0)
1581 *na=1;
1582 else
1583 while (data)
1584 {
1585 if (data->type==t_const)
1586 *(dest++)=strtol(data->v.cstr,&end,0);
1587 data=data->next;
1588 }
1589 }
1590 else if (r->type==t_const && strcmp(r->var,"addr")==0)
1591 {
1592 ok++;
1593 if (addr)
1594 *addr=strtoul(r->v.cstr,&end,0);
1595 }
1596 r=r->next;
1597 }
1598
1599 }
1600 mi_free_results(res);
1601 return ok==2;
1602}
1603
1604mi_asm_insn *mi_parse_insn(mi_results *c)
1605{
1606 mi_asm_insn *res=NULL, *cur=NULL;
1607 mi_results *sub;
1608 char *end;
1609
1610 while (c)
1611 {
1612 if (c->type==t_tuple)
1613 {
1614 if (!res)
1615 res=cur=mi_alloc_asm_insn();
1616 else
1617 {
1618 cur->next=mi_alloc_asm_insn();
1619 cur=cur->next;
1620 }
1621 if (!cur)
1622 {
1623 mi_free_asm_insn(res);
1624 return NULL;
1625 }
1626 sub=c->v.rs;
1627 while (sub)
1628 {
1629 if (sub->type==t_const)
1630 {
1631 if (strcmp(sub->var,"address")==0)
1632 cur->addr=(void *)strtoul(sub->v.cstr,&end,0);
1633 else if (strcmp(sub->var,"func-name")==0)
1634 {
1635 cur->func=sub->v.cstr;
1636 sub->v.cstr=NULL;
1637 }
1638 else if (strcmp(sub->var,"offset")==0)
1639 cur->offset=atoi(sub->v.cstr);
1640 else if (strcmp(sub->var,"inst")==0)
1641 {
1642 cur->inst=sub->v.cstr;
1643 sub->v.cstr=NULL;
1644 }
1645 }
1646 sub=sub->next;
1647 }
1648 }
1649 c=c->next;
1650 }
1651 return res;
1652}
1653
1654mi_asm_insns *mi_parse_insns(mi_results *c)
1655{
1656 mi_asm_insns *res=NULL, *cur=NULL;
1657 mi_results *sub;
1658
1659 while (c)
1660 {
1661 if (c->var)
1662 {
1663 if (strcmp(c->var,"src_and_asm_line")==0 && c->type==t_tuple)
1664 {
1665 if (!res)
1666 res=cur=mi_alloc_asm_insns();
1667 else
1668 {
1669 cur->next=mi_alloc_asm_insns();
1670 cur=cur->next;
1671 }
1672 if (!cur)
1673 {
1674 mi_free_asm_insns(res);
1675 return NULL;
1676 }
1677 sub=c->v.rs;
1678 while (sub)
1679 {
1680 if (sub->var)
1681 {
1682 if (sub->type==t_const)
1683 {
1684 if (strcmp(sub->var,"line")==0)
1685 cur->line=atoi(sub->v.cstr);
1686 else if (strcmp(sub->var,"file")==0)
1687 {
1688 cur->file=sub->v.cstr;
1689 sub->v.cstr=NULL;
1690 }
1691 }
1692 else if (sub->type==t_list)
1693 {
1694 if (strcmp(sub->var,"line_asm_insn")==0)
1695 cur->ins=mi_parse_insn(sub->v.rs);
1696 }
1697 }
1698 sub=sub->next;
1699 }
1700 }
1701 }
1702 else
1703 {/* No source line, just instructions */
1704 res=mi_alloc_asm_insns();
1705 res->ins=mi_parse_insn(c);
1706 break;
1707 }
1708 c=c->next;
1709 }
1710 return res;
1711}
1712
1713
1714mi_asm_insns *mi_get_asm_insns(mi_h *h)
1715{
1716 mi_results *r=mi_res_done_var(h,"asm_insns");
1717 mi_asm_insns *f=NULL;
1718
1719 if (r && r->type==t_list)
1720 f=mi_parse_insns(r->v.rs);
1721 mi_free_results(r);
1722 return f;
1723}
1724
1725mi_chg_reg *mi_parse_list_regs(mi_results *r, int *how_many)
1726{
1727 mi_results *c=r;
1728 int cregs=0;
1729 mi_chg_reg *first=NULL, *cur=NULL;
1730
1731 /* Create the list. */
1732 while (c)
1733 {
1734 if (c->type==t_const && !c->var)
1735 {
1736 if (first)
1737 cur=cur->next=mi_alloc_chg_reg();
1738 else
1739 first=cur=mi_alloc_chg_reg();
1740
1741 if (NULL != cur) {
1742 cur->name=c->v.cstr;
1743 cur->reg=cregs++;
1744 c->v.cstr=NULL;
1745 }
1746 }
1747 c=c->next;
1748 }
1749 if (how_many)
1750 *how_many=cregs;
1751
1752 return first;
1753}
1754
1755mi_chg_reg *mi_get_list_registers(mi_h *h, int *how_many)
1756{
1757 mi_results *r=mi_res_done_var(h,"register-names");
1758 mi_chg_reg *l=NULL;
1759
1760 if (r && r->type==t_list)
1761 l=mi_parse_list_regs(r->v.rs,how_many);
1762 mi_free_results(r);
1763 return l;
1764}
1765
1766mi_chg_reg *mi_parse_list_changed_regs(mi_results *r)
1767{
1768 mi_results *c=r;
1769 mi_chg_reg *first=NULL, *cur=NULL;
1770
1771 /* Create the list. */
1772 while (c)
1773 {
1774 if (c->type==t_const && !c->var)
1775 {
1776 if (first)
1777 cur=cur->next=mi_alloc_chg_reg();
1778 else
1779 first=cur=mi_alloc_chg_reg();
1780 cur->reg=atoi(c->v.cstr);
1781 }
1782 c=c->next;
1783 }
1784
1785 return first;
1786}
1787
1788mi_chg_reg *mi_get_list_changed_regs(mi_h *h)
1789{
1790 mi_results *r=mi_res_done_var(h,"changed-registers");
1791 mi_chg_reg *changed=NULL;
1792
1793 if (r && r->type==t_list)
1794 changed=mi_parse_list_changed_regs(r->v.rs);
1795 mi_free_results(r);
1796 return changed;
1797}
1798
1799int mi_parse_reg_values(mi_results *r, mi_chg_reg *l)
1800{
1801 mi_results *c;
1802
1803 while (r && l)
1804 {
1805 if (r->type==t_tuple && !r->var)
1806 {
1807 c=r->v.rs;
1808 while (c)
1809 {
1810 if (c->type==t_const && c->var)
1811 {
1812 if (strcmp(c->var,"number")==0)
1813 {
1814 if (atoi(c->v.cstr)!=l->reg)
1815 {
1816 mi_error=MI_PARSER;
1817 return 0;
1818 }
1819 }
1820 else if (strcmp(c->var,"value")==0)
1821 {
1822 l->val=c->v.cstr;
1823 c->v.cstr=NULL;
1824 }
1825 }
1826 c=c->next;
1827 }
1828 }
1829 r=r->next;
1830 l=l->next;
1831 }
1832
1833 return !l && !r;
1834}
1835
1836int mi_get_reg_values(mi_h *h, mi_chg_reg *l)
1837{
1838 mi_results *r=mi_res_done_var(h,"register-values");
1839 int ok=0;
1840
1841 if (r && r->type==t_list)
1842 ok=mi_parse_reg_values(r->v.rs,l);
1843 mi_free_results(r);
1844 return ok;
1845}
1846
1847int mi_parse_list_regs_l(mi_results *r, mi_chg_reg *l)
1848{
1849 while (r && l)
1850 {
1851 if (r->type==t_const && !r->var)
1852 {
1853 free(l->name);
1854 l->name=r->v.cstr;
1855 r->v.cstr=NULL;
1856 l=l->next;
1857 }
1858 r=r->next;
1859 }
1860
1861 return !l && !r;
1862}
1863
1864int mi_get_list_registers_l(mi_h *h, mi_chg_reg *l)
1865{
1866 mi_results *r=mi_res_done_var(h,"register-names");
1867 int ok=0;
1868
1869 if (r && r->type==t_list)
1870 ok=mi_parse_list_regs_l(r->v.rs,l);
1871 mi_free_results(r);
1872 return ok;
1873}
1874
1875mi_chg_reg *mi_parse_reg_values_l(mi_results *r, int *how_many)
1876{
1877 mi_results *c;
1878 mi_chg_reg *first=NULL, *cur=NULL;
1879 *how_many=0;
1880
1881 while (r)
1882 {
1883 if (r->type==t_tuple && !r->var)
1884 {
1885 c=r->v.rs;
1886 if (first)
1887 cur=cur->next=mi_alloc_chg_reg();
1888 else
1889 first=cur=mi_alloc_chg_reg();
1890 while (c)
1891 {
1892 if (c->type==t_const && c->var)
1893 {
1894 if (strcmp(c->var,"number")==0)
1895 {
1896 if (NULL != cur)
1897 cur->reg=atoi(c->v.cstr);
1898 (*how_many)++;
1899 }
1900 else if (strcmp(c->var,"value")==0)
1901 {
1902 if (NULL != cur)
1903 cur->val=c->v.cstr;
1904 c->v.cstr=NULL;
1905 }
1906 }
1907 c=c->next;
1908 }
1909 }
1910 r=r->next;
1911 }
1912
1913 return first;
1914}
1915
1916mi_chg_reg *mi_get_reg_values_l(mi_h *h, int *how_many)
1917{
1918 mi_results *r=mi_res_done_var(h,"register-values");
1919 mi_chg_reg *rgs=NULL;
1920
1921 if (r && r->type==t_list)
1922 rgs=mi_parse_reg_values_l(r->v.rs,how_many);
1923 mi_free_results(r);
1924 return rgs;
1925}
1926
1927
diff --git a/pathologist/src/mi/gdbmi_prg_control.c b/pathologist/src/mi/gdbmi_prg_control.c
new file mode 100644
index 0000000..80ac7af
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_prg_control.c
@@ -0,0 +1,598 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: Program control.
7 Comments:
8 GDB/MI commands for the "Program Control" section. @<p>
9
10@<pre>
11gdb command: Implemented?
12
13-exec-abort N.A. (*) (kill, but with non-interactive options)
14-exec-arguments Yes
15-exec-continue Yes ASYNC
16-exec-finish Yes ASYNC
17-exec-interrupt Yes ASYNC
18-exec-next Yes ASYNC
19-exec-next-instruction Yes ASYNC
20-exec-return Yes
21-exec-run Yes ASYNC
22-exec-show-arguments N.A. (show args) see gmi_stack_info_frame
23-exec-step Yes ASYNC
24-exec-step-instruction Yes ASYNC
25-exec-until Yes ASYNC
26-file-exec-and-symbols Yes
27-file-exec-file No
28-file-list-exec-sections N.A. (info file)
29-file-list-exec-source-files N.A.
30-file-list-shared-libraries N.A.
31-file-list-symbol-files N.A.
32-file-symbol-file Yes
33@</pre>
34
35(*) gmi_exec_kill implements it, but you should ensure that
36gmi_gdb_set("confirm","off") was called. @<p>
37
38GDB Bug workaround for -file-exec-and-symbols and -file-symbol-file: This
39is complex, but a real bug. When you set a breakpoint you never know the
40name of the file as it appears in the debug info. So you can be specifying
41an absolute file name or a relative file name. The reference point could be
42different than the one used in the debug info. To solve all the combinations
43gdb does a search trying various combinations. GDB isn't very smart so you
44must at least specify the working directory and the directory where the
45binary is located to get a good chance (+ user options to solve the rest).
46Once you did it gdb can find the file by doing transformations to the
47"canonical" filename. This search works OK for already loaded symtabs
48(symbol tables), but it have a bug when the search is done for psymtabs
49(partial symtabs). The bug is in the use of source_full_path_of (source.c).
50This function calls openp indicating try_cwd_first. It makes the search file
51if the psymtab file name have at least one dirseparator. It means that
52psymtabs for files compiled with relative paths will fail. The search for
53symtabs uses symtab_to_filename, it calls open_source_file which finally
54calls openp without try_cwd_first.@*
55To workaround this bug we must ensure gdb loads *all* the symtabs to memory.
56And here comes another problem -file-exec-and-symbols doesn't support it
57according to docs. In real life that's a wrapper for "file", but as nobody
58can say it won't change we must use the CLI command.
59
60***************************************************************************/
61
62#include <signal.h>
63#include "gdbmi.h"
64
65/* Low level versions. */
66
67void mi_file_exec_and_symbols(mi_h *h, const char *file)
68{
69 if (mi_get_workaround(MI_PSYM_SEARCH))
70 mi_send(h,"file %s -readnow\n",file);
71 else
72 mi_send(h,"-file-exec-and-symbols %s\n",file);
73}
74
75void mi_set_unwind_on_signal_on(mi_h *h)
76{
77 mi_send(h, "set unwindonsignal on\n");
78}
79
80void mi_exec_arguments(mi_h *h, const char *args)
81{
82 mi_send(h,"-exec-arguments %s\n",args);
83}
84
85void mi_exec_run(mi_h *h)
86{
87 mi_send(h,"-exec-run\n");
88}
89
90void mi_exec_continue(mi_h *h)
91{
92 mi_send(h,"-exec-continue\n");
93}
94
95void mi_exec_continue_reverse(mi_h *h)
96{
97 mi_send(h, "-exec-continue --reverse\n");
98}
99
100void mi_target_terminal(mi_h *h, const char *tty_name)
101{
102 mi_send(h,"tty %s\n",tty_name);
103}
104
105void mi_file_symbol_file(mi_h *h, const char *file)
106{
107 if (mi_get_workaround(MI_PSYM_SEARCH))
108 mi_send(h,"symbol-file %s -readnow\n",file);
109 else
110 mi_send(h,"-file-symbol-file %s\n",file);
111}
112
113void mi_exec_finish(mi_h *h)
114{
115 mi_send(h,"-exec-finish\n");
116}
117
118void mi_exec_finish_reverse(mi_h * h)
119{
120 mi_send(h, "-exec-finish --reverse\n");
121}
122void mi_exec_interrupt(mi_h *h)
123{
124 mi_send(h,"-exec-interrupt\n");
125}
126
127void mi_exec_record_process(mi_h *h)
128{
129mi_send(h, "-interpreter-exec mi record\n");
130}
131
132void mi_exec_record_stop(mi_h *h)
133{
134mi_send(h, "-interpreter-exec mi record stop\n");
135}
136
137void mi_exec_next(mi_h *h, int count)
138{
139 if (count>1)
140 mi_send(h,"-exec-next %d\n",count);
141 else
142 mi_send(h,"-exec-next\n");
143}
144
145void mi_exec_next_instruction(mi_h *h)
146{
147 mi_send(h,"-exec-next-instruction\n");
148}
149
150void mi_exec_step(mi_h *h, int count)
151{
152 if (count>1)
153 mi_send(h,"-exec-step %d\n",count);
154 else
155 mi_send(h,"-exec-step\n");
156}
157
158
159void mi_exec_step_back(mi_h *h, int count)
160{
161 if (count>1)
162 mi_send(h, "-exec-step %d --reverse\n", count);
163 else
164 mi_send(h, "-exec-step --reverse\n");
165}
166
167
168void mi_exec_next_reverse(mi_h *h, int count)
169{
170 if (count>1)
171 mi_send(h, "-exec-next %d --reverse\n", count);
172 else
173 mi_send(h, "-exec-next --reverse\n");
174}
175
176
177void mi_exec_step_instruction(mi_h *h)
178{
179 mi_send(h,"-exec-step-instruction\n");
180}
181
182void mi_exec_until(mi_h *h, const char *file, int line)
183{
184 if (!file)
185 mi_send(h,"-exec-until\n");
186 else
187 mi_send(h,"-exec-until %s:%d\n",file,line);
188}
189
190void mi_exec_until_addr(mi_h *h, void *addr)
191{
192 mi_send(h,"-exec-until *%p\n",addr);
193}
194
195void mi_exec_return(mi_h *h)
196{
197 mi_send(h,"-exec-return\n");
198}
199
200void mi_exec_kill(mi_h *h)
201{
202 mi_send(h,"kill\n");
203}
204
205/* High level versions. */
206
207/**[txh]********************************************************************
208
209 Description:
210 Specify the executable and arguments for local debug.
211
212 Command: -file-exec-and-symbols + -exec-arguments
213 Return: !=0 OK
214
215***************************************************************************/
216
217int gmi_set_exec(mi_h *h, const char *file, const char *args)
218{
219 mi_file_exec_and_symbols(h,file);
220 if (!mi_res_simple_done(h))
221 return 0;
222 if (!args)
223 return 1;
224 mi_exec_arguments(h,args);
225 return mi_res_simple_done(h);
226}
227
228int gmi_set_unwind_on_signal_on(mi_h * h)
229{
230 mi_set_unwind_on_signal_on(h);
231 return mi_res_simple_running(h);
232}
233
234/**[txh]********************************************************************
235
236 Description:
237 Start running the executable. Remote sessions starts running.
238
239 Command: -exec-run
240 Return: !=0 OK
241
242***************************************************************************/
243
244int gmi_exec_run(mi_h *h)
245{
246 mi_exec_run(h);
247 return mi_res_simple_running(h);
248}
249
250/**[txh]********************************************************************
251
252 Description:
253 Continue the execution after a "stop".
254
255 Command: -exec-continue
256 Return: !=0 OK
257
258***************************************************************************/
259
260int gmi_exec_continue(mi_h *h)
261{
262 mi_exec_continue(h);
263 return mi_res_simple_running(h);
264}
265
266/**[txh]********************************************************************
267
268 Description:
269 Continue the execution in reverse order after a "stop".
270
271 Command: -exec-continue --reverse
272 Return: !=0 OK
273
274***************************************************************************/
275int gmi_exec_continue_reverse(mi_h *h)
276{
277 mi_exec_continue_reverse(h);
278 return mi_res_simple_running(h);
279}
280
281/**[txh]********************************************************************
282
283 Description:
284 Indicate which terminal will use the target program. For local sessions.
285
286 Command: tty
287 Return: !=0 OK
288 Example:
289
290***************************************************************************/
291
292int gmi_target_terminal(mi_h *h, const char *tty_name)
293{
294 mi_target_terminal(h,tty_name);
295 return mi_res_simple_done(h);
296}
297
298/**[txh]********************************************************************
299
300 Description:
301 Specify what's the local copy that have debug info. For remote sessions.
302
303 Command: -file-symbol-file
304 Return: !=0 OK
305
306***************************************************************************/
307
308int gmi_file_symbol_file(mi_h *h, const char *file)
309{
310 mi_file_symbol_file(h,file);
311 return mi_res_simple_done(h);
312}
313
314/**[txh]********************************************************************
315
316 Description:
317 Continue until function return, the return value is included in the async
318response.
319
320 Command: -exec-finish
321 Return: !=0 OK.
322
323***************************************************************************/
324
325int gmi_exec_finish(mi_h *h)
326{
327 mi_exec_finish(h);
328 return mi_res_simple_running(h);
329}
330
331/**[txh]********************************************************************
332
333 Description:
334 Continue backwardly until function return, the return value is included in
335 the async response.
336
337 Command: -exec-finish --reverse
338 Return: !=0 OK.
339
340***************************************************************************/
341int gmi_exec_finish_reverse(mi_h *h)
342{
343 mi_exec_finish_reverse(h);
344 return mi_res_simple_running(h);
345}
346
347/**[txh]********************************************************************
348
349 Description:
350 Stop the program using SIGINT. The corresponding command should be
351-exec-interrupt but not even gdb 6.1.1 can do it because the "async" mode
352isn't really working.
353
354 Command: -exec-interrupt [replacement]
355 Return: Always 1
356 Example:
357
358***************************************************************************/
359
360int gmi_exec_interrupt(mi_h *h)
361{
362 // **** IMPORTANT!!! **** Not even gdb 6.1.1 can do it because the "async"
363 // mode isn't really working.
364 //mi_exec_interrupt(h);
365 //return mi_res_simple_running(h);
366
367 kill(h->pid,SIGINT);
368 return 1; // How can I know?
369}
370
371/**[txh]********************************************************************
372
373 Description:
374 Next line of code.
375
376 Command: -exec-next
377 Return: !=0 OK
378
379***************************************************************************/
380
381int gmi_exec_next(mi_h *h)
382{
383 mi_exec_next(h,1);
384 return mi_res_simple_running(h);
385}
386
387/**[txh]********************************************************************
388
389 Description:
390 Start recording a process
391
392 Command: -interpreter-exec mi record
393 Return: !=0 OK
394
395***************************************************************************/
396int gmi_exec_record_process(mi_h *h)
397{
398 mi_exec_record_process(h);
399 return 1;
400}
401
402/**[txh]********************************************************************
403
404 Description:
405 Start recording a process
406
407 Command: -interpreter-exec mi record stop
408 Return: !=0 OK
409
410***************************************************************************/
411int gmi_exec_record_stop(mi_h *h)
412{
413 mi_exec_record_stop(h);
414 return 1;
415}
416
417/**[txh]********************************************************************
418
419 Description:
420 Skip count lines of code.
421
422 Command: -exec-next count
423 Return: !=0 OK
424
425***************************************************************************/
426
427int gmi_exec_next_cnt(mi_h *h, int count)
428{
429 mi_exec_next(h,count);
430 return mi_res_simple_running(h);
431}
432
433/**[txh]********************************************************************
434
435 Description:
436 Next line of assembler code.
437
438 Command: -exec-next-instruction
439 Return: !=0 OK
440
441***************************************************************************/
442
443int gmi_exec_next_instruction(mi_h *h)
444{
445 mi_exec_next_instruction(h);
446 return mi_res_simple_running(h);
447}
448
449/**[txh]********************************************************************
450
451 Description:
452 Next line of code. Get inside functions.
453
454 Command: -exec-step
455 Return: !=0 OK
456
457***************************************************************************/
458
459int gmi_exec_step(mi_h *h)
460{
461 mi_exec_step(h,1);
462 return mi_res_simple_running(h);
463}
464
465
466
467/**[txh]********************************************************************
468
469 Description:
470 Previous line of code. Get inside functions.
471
472 Command: -exec-step --reverse
473 Return: !=0 OK
474
475***************************************************************************/
476int gmi_exec_step_back(mi_h *h)
477{
478 mi_exec_step_back(h,1);
479 return mi_res_simple_running(h);
480}
481
482
483/**[txh]********************************************************************
484
485 Description:
486 Previous line of code (do not step into).
487
488 Command: -exec-next --reverse
489 Return: !=0 OK
490
491***************************************************************************/
492int gmi_exec_next_reverse(mi_h *h)
493{
494 mi_exec_next_reverse(h,1);
495 return mi_res_simple_running(h);
496}
497
498
499/**[txh]********************************************************************
500
501 Description:
502 Next count lines of code. Get inside functions.
503
504 Command: -exec-step count
505 Return: !=0 OK
506
507***************************************************************************/
508
509int gmi_exec_step_cnt(mi_h *h, int count)
510{
511 mi_exec_step(h,count);
512 return mi_res_simple_running(h);
513}
514
515/**[txh]********************************************************************
516
517 Description:
518 Next line of assembler code. Get inside calls.
519
520 Command: -exec-step-instruction
521 Return: !=0 OK
522
523***************************************************************************/
524
525int gmi_exec_step_instruction(mi_h *h)
526{
527 mi_exec_step_instruction(h);
528 return mi_res_simple_running(h);
529}
530
531/**[txh]********************************************************************
532
533 Description:
534 Execute until location is reached. If file is NULL then is until next
535line.
536
537 Command: -exec-until
538 Return: !=0 OK
539
540***************************************************************************/
541
542int gmi_exec_until(mi_h *h, const char *file, int line)
543{
544 mi_exec_until(h,file,line);
545 return mi_res_simple_running(h);
546}
547
548/**[txh]********************************************************************
549
550 Description:
551 Execute until location is reached.
552
553 Command: -exec-until (using *address)
554 Return: !=0 OK
555
556***************************************************************************/
557
558int gmi_exec_until_addr(mi_h *h, void *addr)
559{
560 mi_exec_until_addr(h,addr);
561 return mi_res_simple_running(h);
562}
563
564/**[txh]********************************************************************
565
566 Description:
567 Return to previous frame inmediatly.
568
569 Command: -exec-return
570 Return: A pointer to a new mi_frames structure indicating the current
571location. NULL on error.
572
573***************************************************************************/
574
575mi_frames *gmi_exec_return(mi_h *h)
576{
577 mi_exec_return(h);
578 return mi_res_frame(h);
579}
580
581/**[txh]********************************************************************
582
583 Description:
584 Just kill the program. That's what -exec-abort should do, but it isn't
585implemented by gdb. This implementation only works if the interactive mode
586is disabled (gmi_gdb_set("confirm","off")).
587
588 Command: -exec-abort [using kill]
589 Return: !=0 OK
590
591***************************************************************************/
592
593int gmi_exec_kill(mi_h *h)
594{
595 mi_exec_kill(h);
596 return mi_res_simple_done(h);
597}
598
diff --git a/pathologist/src/mi/gdbmi_stack_man.c b/pathologist/src/mi/gdbmi_stack_man.c
new file mode 100644
index 0000000..977ea5e
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_stack_man.c
@@ -0,0 +1,222 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: Stack manipulation.
7 Comments:
8 GDB/MI commands for the "Stack Manipulation" section. @<p>
9
10@<pre>
11gdb command: Implemented?
12
13-stack-info-frame Yes, implemented as "frame"
14-stack-info-depth Yes
15-stack-list-arguments Yes
16-stack-list-frames Yes
17-stack-list-locals Yes
18-stack-select-frame Yes
19@</pre>
20
21***************************************************************************/
22
23#include "gdbmi.h"
24
25/* Low level versions. */
26
27void mi_stack_list_frames(mi_h *h, int from, int to)
28{
29 if (from<0)
30 mi_send(h,"-stack-list-frames\n");
31 else
32 mi_send(h,"-stack-list-frames %d %d\n",from,to);
33}
34
35void mi_stack_list_arguments(mi_h *h, int show, int from, int to)
36{
37 if (from<0)
38 mi_send(h,"-stack-list-arguments %d\n",show);
39 else
40 mi_send(h,"-stack-list-arguments %d %d %d\n",show,from,to);
41}
42
43void mi_stack_info_frame(mi_h *h)
44{
45 mi_send(h,"frame\n");
46}
47
48void mi_stack_info_depth(mi_h *h, int depth)
49{
50 if (depth<0)
51 mi_send(h,"-stack-info-depth\n");
52 else
53 mi_send(h,"-stack-info-depth %d\n",depth);
54}
55
56void mi_stack_select_frame(mi_h *h, int framenum)
57{
58 mi_send(h,"-stack-select-frame %d\n",framenum);
59}
60
61void mi_stack_list_locals(mi_h *h, int show)
62{
63 mi_send(h,"-stack-list-locals %d\n",show);
64}
65
66/* High level versions. */
67
68/**[txh]********************************************************************
69
70 Description:
71 List of frames. Arguments aren't filled.
72
73 Command: -stack-list-frames
74 Return: A new list of mi_frames or NULL on error.
75
76***************************************************************************/
77
78mi_frames *gmi_stack_list_frames(mi_h *h)
79{
80 mi_stack_list_frames(h,-1,-1);
81 return mi_res_frames_array(h,"stack");
82}
83
84/**[txh]********************************************************************
85
86 Description:
87 List of frames. Arguments aren't filled. Only the frames in the from
88 - to range are returned.
89
90 Command: -stack-list-frames
91 Return: A new list of mi_frames or NULL on error.
92
93***************************************************************************/
94
95mi_frames *gmi_stack_list_frames_r(mi_h *h, int from, int to)
96{
97 mi_stack_list_frames(h,from,to);
98 return mi_res_frames_array(h,"stack");
99}
100
101/**[txh]********************************************************************
102
103 Description:
104 List arguments. Only level and args filled.
105
106 Command: -stack-list-arguments
107 Return: A new list of mi_frames or NULL on error.
108
109***************************************************************************/
110
111mi_frames *gmi_stack_list_arguments(mi_h *h, int show)
112{
113 mi_stack_list_arguments(h,show,-1,-1);
114 return mi_res_frames_array(h,"stack-args");
115}
116
117/**[txh]********************************************************************
118
119 Description:
120 List arguments. Only level and args filled. Only for the
121frames in the from - to range.
122
123 Command: -stack-list-arguments
124 Return: A new list of mi_frames or NULL on error.
125
126***************************************************************************/
127
128mi_frames *gmi_stack_list_arguments_r(mi_h *h, int show, int from, int to)
129{
130 mi_stack_list_arguments(h,show,from,to);
131 return mi_res_frames_array(h,"stack-args");
132}
133
134/**[txh]********************************************************************
135
136 Description:
137 Information about the current frame, including args.
138
139 Command: -stack-info-frame [using frame]
140 Return: A new mi_frames or NULL on error.
141
142***************************************************************************/
143
144mi_frames *gmi_stack_info_frame(mi_h *h)
145{
146 mi_stack_info_frame(h);
147 return mi_res_frame(h);
148}
149
150/**[txh]********************************************************************
151
152 Description:
153 Stack info depth.
154
155 Command: -stack-info-depth
156 Return: The depth or -1 on error.
157
158***************************************************************************/
159
160int gmi_stack_info_depth(mi_h *h, int max_depth)
161{
162 mi_results *r;
163 int ret=-1;
164
165 mi_stack_info_depth(h,max_depth);
166 r=mi_res_done_var(h,"depth");
167 if (r && r->type==t_const)
168 {
169 ret=atoi(r->v.cstr);
170 mi_free_results(r);
171 }
172 return ret;
173}
174
175/**[txh]********************************************************************
176
177 Description:
178 Set stack info depth.
179
180 Command: -stack-info-depth [no args]
181 Return: The depth or -1 on error.
182 Example:
183
184***************************************************************************/
185
186int gmi_stack_info_depth_get(mi_h *h)
187{
188 return gmi_stack_info_depth(h,-1);
189}
190
191/**[txh]********************************************************************
192
193 Description:
194 Change current frame.
195
196 Command: -stack-select-frame
197 Return: !=0 OK
198
199***************************************************************************/
200
201int gmi_stack_select_frame(mi_h *h, int framenum)
202{
203 mi_stack_select_frame(h,framenum);
204 return mi_res_simple_done(h);
205}
206
207/**[txh]********************************************************************
208
209 Description:
210 List of local vars.
211
212 Command: -stack-list-locals
213 Return: A new mi_results tree containing the variables or NULL on error.
214
215***************************************************************************/
216
217mi_results *gmi_stack_list_locals(mi_h *h, int show)
218{
219 mi_stack_list_locals(h,show);
220 return mi_res_done_var(h,"locals");
221}
222
diff --git a/pathologist/src/mi/gdbmi_symbol_query.c b/pathologist/src/mi/gdbmi_symbol_query.c
new file mode 100644
index 0000000..8910517
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_symbol_query.c
@@ -0,0 +1,32 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: Symbol query.
7 Comments:
8 GDB/MI commands for the "Symbol Query" section.@p
9
10@<pre>
11gdb command: Implemented?
12-symbol-info-address N.A. (info address, human readable)
13-symbol-info-file N.A.
14-symbol-info-function N.A.
15-symbol-info-line N.A. (info line, human readable)
16-symbol-info-symbol N.A. (info symbol, human readable)
17-symbol-list-functions N.A. (info functions, human readable)
18-symbol-list-types N.A. (info types, human readable)
19-symbol-list-variables N.A. (info variables, human readable)
20-symbol-list-lines No (gdb 6.x)
21-symbol-locate N.A.
22-symbol-type N.A. (ptype, human readable)
23@</pre>
24
25Note:@p
26
27Only one is implemented and not in gdb 5.x.@p
28
29***************************************************************************/
30
31#include "gdbmi.h"
32
diff --git a/pathologist/src/mi/gdbmi_target_man.c b/pathologist/src/mi/gdbmi_target_man.c
new file mode 100644
index 0000000..bbb2b98
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_target_man.c
@@ -0,0 +1,119 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004-2007 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: Target manipulation.
7 Comments:
8 GDB/MI commands for the "Target Manipulation" section. @<p>
9
10@<pre>
11-target-attach Yes (implemented using attach)
12-target-compare-sections N.A. (compare-sections)
13-target-detach Yes
14-target-download Yes
15-target-exec-status N.A.
16-target-list-available-targets N.A. (help target)
17-target-list-current-targets N.A. (info file among other things)
18-target-list-parameters N.A.
19-target-select Yes
20@</pre>
21
22***************************************************************************/
23
24#include "gdbmi.h"
25
26/* Low level versions. */
27
28void mi_target_select(mi_h *h, const char *type, const char *params)
29{
30 mi_send(h,"-target-select %s %s\n",type,params);
31}
32
33/* Note: -target-attach isn't currently implemented :-( (gdb 6.1.1) */
34void mi_target_attach(mi_h *h, pid_t pid)
35{
36 mi_send(h,"attach %d\n",pid);
37}
38
39void mi_target_detach(mi_h *h)
40{
41 mi_send(h,"-target-detach\n");
42}
43
44void mi_target_download(mi_h *h)
45{
46 mi_send(h,"-target-download\n");
47}
48
49/* High level versions. */
50
51/**[txh]********************************************************************
52
53 Description:
54 Connect to a remote gdbserver using the specified methode.
55
56 Command: -target-select
57 Return: !=0 OK
58
59***************************************************************************/
60
61int gmi_target_select(mi_h *h, const char *type, const char *params)
62{
63 mi_target_select(h,type,params);
64 if (!mi_res_simple_connected(h))
65 return 0;
66 mi_send_target_commands(h);
67 return 1;
68}
69
70/**[txh]********************************************************************
71
72 Description:
73 Attach to an already running process.
74
75 Command: -target-attach [using attach]
76 Return: The frame of the current location, NULL on error.
77
78***************************************************************************/
79
80mi_frames *gmi_target_attach(mi_h *h, pid_t pid)
81{
82 mi_target_attach(h,pid);
83 //return mi_res_simple_done(h);
84 return mi_res_frame(h);
85}
86
87/**[txh]********************************************************************
88
89 Description:
90 Detach from an attached process.
91
92 Command: -target-detach
93 Return: !=0 OK
94
95***************************************************************************/
96
97int gmi_target_detach(mi_h *h)
98{
99 mi_target_detach(h);
100 return mi_res_simple_done(h);
101}
102
103/**[txh]********************************************************************
104
105 Description:
106 Loads the executable onto the remote target.
107
108 Command: -target-download
109 Return: !=0 OK
110
111***************************************************************************/
112
113int gmi_target_download(mi_h *h)
114{
115 mi_target_download(h);
116 // TODO: this response have some data
117 return mi_res_simple_done(h);
118}
119
diff --git a/pathologist/src/mi/gdbmi_thread.c b/pathologist/src/mi/gdbmi_thread.c
new file mode 100644
index 0000000..b8fa63d
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_thread.c
@@ -0,0 +1,89 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: Thread commands.
7 Comments:
8 GDB/MI commands for the "Thread Commands" section. @<p>
9
10@<pre>
11gdb command: Implemented?
12-thread-info N.A.
13-thread-list-all-threads Yes, implemented as "info threads"
14-thread-list-ids Yes
15-thread-select Yes
16@</pre>
17
18***************************************************************************/
19
20#include "gdbmi.h"
21
22/* Low level versions. */
23
24void mi_thread_list_ids(mi_h *h)
25{
26 mi_send(h,"-thread-list-ids\n");
27}
28
29void mi_thread_select(mi_h *h, int id)
30{
31 mi_send(h,"-thread-select %d\n",id);
32}
33
34void mi_thread_list_all_threads(mi_h *h)
35{
36 mi_send(h,"info threads\n");
37}
38
39/* High level versions. */
40
41/**[txh]********************************************************************
42
43 Description:
44 List available thread ids.
45
46 Command: -thread-list-ids
47 Return: !=0 OK
48
49***************************************************************************/
50
51int gmi_thread_list_ids(mi_h *h, int **list)
52{
53 mi_thread_list_ids(h);
54 return mi_res_thread_ids(h,list);
55}
56
57/**[txh]********************************************************************
58
59 Description:
60 Select a thread.
61
62 Command: -thread-select
63 Return: A new mi_frames or NULL on error.
64
65***************************************************************************/
66
67mi_frames *gmi_thread_select(mi_h *h, int id)
68{
69 mi_thread_select(h,id);
70 return mi_res_frame(h);
71}
72
73/**[txh]********************************************************************
74
75 Description:
76 Get a list of frames for each available thread. Implemented using "info
77thread".
78
79 Command: -thread-list-all-threads
80 Return: A kist of frames, NULL on error
81
82***************************************************************************/
83
84mi_frames *gmi_thread_list_all_threads(mi_h *h)
85{
86 mi_thread_list_all_threads(h);
87 return mi_res_frames_list(h);
88}
89
diff --git a/pathologist/src/mi/gdbmi_var_obj.c b/pathologist/src/mi/gdbmi_var_obj.c
new file mode 100644
index 0000000..435feec
--- /dev/null
+++ b/pathologist/src/mi/gdbmi_var_obj.c
@@ -0,0 +1,371 @@
1/**[txh]********************************************************************
2
3 Copyright (c) 2004 by Salvador E. Tropea.
4 Covered by the GPL license.
5
6 Module: Variable objects.
7 Comments:
8 GDB/MI commands for the "Variable Objects" section.
9 @<p>
10
11@<pre>
12gdb command: Imp? Description:
13-var-create Yes create a variable object
14-var-delete Yes delete the variable object and its children
15-var-set-format Yes set the display format of this variable
16-var-show-format Yes show the display format of this variable
17-var-info-num-children Yes tells how many children this object has
18-var-list-children Yes* return a list of the object's children
19-var-info-type Yes show the type of this variable object
20-var-info-expression Yes print what this variable object represents
21-var-show-attributes Yes is this variable editable?
22-var-evaluate-expression Yes get the value of this variable
23-var-assign Yes set the value of this variable
24-var-update Yes* update the variable and its children
25@</pre>
26
27Notes: @<p>
281) I suggest letting gdb to choose the names for the variables.@*
292) -var-list-children supports an optional "show values" argument in MI v2.
30It isn't implemented.@*
31
32 @<p>
33
34* MI v1 and v2 result formats supported. @<p>
35
36***************************************************************************/
37
38#include "gdbmi.h"
39
40/* Low level versions. */
41
42void mi_var_create(mi_h *h, const char *name, int frame, const char *exp)
43{
44 const char *n=name ? name : "-";
45
46 if (frame<0)
47 mi_send(h,"-var-create %s * %s\n",n,exp);
48 else
49 mi_send(h,"-var-create %s %d %s\n",n,frame,exp);
50}
51
52void mi_var_delete(mi_h *h, const char *name)
53{
54 mi_send(h,"-var-delete %s\n",name);
55}
56
57void mi_var_set_format(mi_h *h, const char *name, const char *format)
58{
59 mi_send(h,"-var-set-format \"%s\" %s\n",name,format);
60}
61
62void mi_var_show_format(mi_h *h, const char *name)
63{
64 mi_send(h,"-var-show-format \"%s\"\n",name);
65}
66
67void mi_var_info_num_children(mi_h *h, const char *name)
68{
69 mi_send(h,"-var-info-num-children \"%s\"\n",name);
70}
71
72void mi_var_info_type(mi_h *h, const char *name)
73{
74 mi_send(h,"-var-info-type \"%s\"\n",name);
75}
76
77void mi_var_info_expression(mi_h *h, const char *name)
78{
79 mi_send(h,"-var-info-expression \"%s\"\n",name);
80}
81
82void mi_var_show_attributes(mi_h *h, const char *name)
83{
84 mi_send(h,"-var-show-attributes \"%s\"\n",name);
85}
86
87void mi_var_update(mi_h *h, const char *name)
88{
89 if (name)
90 mi_send(h,"-var-update %s\n",name);
91 else
92 mi_send(h,"-var-update *\n");
93}
94
95void mi_var_assign(mi_h *h, const char *name, const char *expression)
96{
97 mi_send(h,"-var-assign \"%s\" \"%s\"\n",name,expression);
98}
99
100void mi_var_evaluate_expression(mi_h *h, const char *name)
101{
102 mi_send(h,"-var-evaluate-expression \"%s\"\n",name);
103}
104
105void mi_var_list_children(mi_h *h, const char *name)
106{
107 if (h->version>=MI_VERSION2U(2,0,0))
108 mi_send(h,"-var-list-children --all-values \"%s\"\n",name);
109 else
110 mi_send(h,"-var-list-children \"%s\"\n",name);
111}
112
113/* High level versions. */
114
115/**[txh]********************************************************************
116
117 Description:
118 Create a variable object. I recommend using gmi_var_create and letting
119gdb choose the names.
120
121 Command: -var-create
122 Return: A new mi_gvar strcture or NULL on error.
123
124***************************************************************************/
125
126mi_gvar *gmi_var_create_nm(mi_h *h, const char *name, int frame, const char *exp)
127{
128 mi_var_create(h,name,frame,exp);
129 return mi_res_gvar(h,NULL,exp);
130}
131
132/**[txh]********************************************************************
133
134 Description:
135 Create a variable object. The name is selected by gdb. Alternative:
136 gmi_full_var_create.
137
138 Command: -var-create [auto name]
139 Return: A new mi_gvar strcture or NULL on error.
140
141***************************************************************************/
142
143mi_gvar *gmi_var_create(mi_h *h, int frame, const char *exp)
144{
145 return gmi_var_create_nm(h,NULL,frame,exp);
146}
147
148/**[txh]********************************************************************
149
150 Description:
151 Delete a variable object. Doesn't free the mi_gvar data.
152
153 Command: -var-delete
154 Return: !=0 OK
155
156***************************************************************************/
157
158int gmi_var_delete(mi_h *h, mi_gvar *var)
159{
160 mi_var_delete(h,var->name);
161 return mi_res_simple_done(h);
162}
163
164/**[txh]********************************************************************
165
166 Description:
167 Set the format used to represent the result.
168
169 Command: -var-set-format
170 Return: !=0 OK
171
172***************************************************************************/
173
174int gmi_var_set_format(mi_h *h, mi_gvar *var, enum mi_gvar_fmt format)
175{
176 int ret;
177
178 mi_var_set_format(h,var->name,mi_format_enum_to_str(format));
179 ret=mi_res_simple_done(h);
180 if (ret)
181 var->format=format;
182 return ret;
183}
184
185/**[txh]********************************************************************
186
187 Description:
188 Fill the format field with info from gdb.
189
190 Command: -var-show-format
191 Return: !=0 OK.
192
193***************************************************************************/
194
195int gmi_var_show_format(mi_h *h, mi_gvar *var)
196{
197 mi_var_show_format(h,var->name);
198 return mi_res_gvar(h,var,NULL)!=NULL;
199}
200
201/**[txh]********************************************************************
202
203 Description:
204 Fill the numchild field with info from gdb.
205
206 Command: -var-info-num-children
207 Return: !=0 OK
208
209***************************************************************************/
210
211int gmi_var_info_num_children(mi_h *h, mi_gvar *var)
212{
213 mi_var_info_num_children(h,var->name);
214 return mi_res_gvar(h,var,NULL)!=NULL;
215}
216
217/**[txh]********************************************************************
218
219 Description:
220 Fill the type field with info from gdb.
221
222 Command: -var-info-type
223 Return: !=0 OK
224
225***************************************************************************/
226
227int gmi_var_info_type(mi_h *h, mi_gvar *var)
228{
229 mi_var_info_type(h,var->name);
230 return mi_res_gvar(h,var,NULL)!=NULL;
231}
232
233/**[txh]********************************************************************
234
235 Description:
236 Fill the expression and lang fields with info from gdb. Note that lang
237isn't filled during creation.
238
239 Command: -var-info-expression
240 Return: !=0 OK
241
242***************************************************************************/
243
244int gmi_var_info_expression(mi_h *h, mi_gvar *var)
245{
246 mi_var_info_expression(h,var->name);
247 return mi_res_gvar(h,var,NULL)!=NULL;
248}
249
250
251/**[txh]********************************************************************
252
253 Description:
254 Fill the attr field with info from gdb. Note that attr isn't filled
255during creation.
256
257 Command: -var-show-attributes
258 Return: !=0 OK
259
260***************************************************************************/
261
262int gmi_var_show_attributes(mi_h *h, mi_gvar *var)
263{
264 mi_var_show_attributes(h,var->name);
265 return mi_res_gvar(h,var,NULL)!=NULL;
266}
267
268/**[txh]********************************************************************
269
270 Description:
271 Create the variable and also fill the lang and attr fields. The name is
272selected by gdb.
273
274 Command: -var-create + -var-info-expression + -var-show-attributes
275 Return: A new mi_gvar strcture or NULL on error.
276
277***************************************************************************/
278
279mi_gvar *gmi_full_var_create(mi_h *h, int frame, const char *exp)
280{
281 mi_gvar *var=gmi_var_create_nm(h,NULL,frame,exp);
282 if (var)
283 {/* What if it fails? */
284 gmi_var_info_expression(h,var);
285 gmi_var_show_attributes(h,var);
286 }
287 return var;
288}
289
290/**[txh]********************************************************************
291
292 Description:
293 Update variable. Use NULL for all. Note that *changed can be NULL if none
294updated.
295
296 Command: -var-update
297 Return: !=0 OK. The changed list contains the list of changed vars.
298
299***************************************************************************/
300
301int gmi_var_update(mi_h *h, mi_gvar *var, mi_gvar_chg **changed)
302{
303 mi_var_update(h,var ? var->name : NULL);
304 return mi_res_changelist(h,changed);
305}
306
307/**[txh]********************************************************************
308
309 Description:
310 Change variable. The new value replaces the value field.
311
312 Command: -var-assign
313 Return: !=0 OK
314
315***************************************************************************/
316
317int gmi_var_assign(mi_h *h, mi_gvar *var, const char *expression)
318{
319 char *res;
320 mi_var_assign(h,var->name,expression);
321 res=mi_res_value(h);
322 if (res)
323 {
324 free(var->value);
325 var->value=res;
326 return 1;
327 }
328 return 0;
329}
330
331/**[txh]********************************************************************
332
333 Description:
334 Fill the value field getting the current value for a variable.
335
336 Command: -var-evaluate-expression
337 Return: !=0 OK, value contains the result.
338
339***************************************************************************/
340
341int gmi_var_evaluate_expression(mi_h *h, mi_gvar *var)
342{
343 char *s;
344
345 mi_var_evaluate_expression(h,var->name);
346 s=mi_res_value(h);
347 if (s)
348 {
349 free(var->value);
350 var->value=s;
351 }
352 return s!=NULL;
353}
354
355/**[txh]********************************************************************
356
357 Description:
358 List children. It ONLY returns the first level information. :-(@*
359 On success the child field contains the list of children.
360
361 Command: -var-list-children
362 Return: !=0 OK
363
364***************************************************************************/
365
366int gmi_var_list_children(mi_h *h, mi_gvar *var)
367{
368 mi_var_list_children(h,var->name);
369 return mi_res_children(h,var);
370}
371
diff --git a/pathologist/src/minixml/Makefile.am b/pathologist/src/minixml/Makefile.am
new file mode 100644
index 0000000..423fbdc
--- /dev/null
+++ b/pathologist/src/minixml/Makefile.am
@@ -0,0 +1,27 @@
1INCLUDES = -I$(top_srcdir)/src/include -I$(top_builddir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = --coverage -O0
9 XLIB = -lgcov
10endif
11
12lib_LTLIBRARIES = libminixml.la
13
14libminixml_la_SOURCES = \
15 mxml-attr.c \
16 mxmldoc.c \
17 mxml-entity.c \
18 mxml-file.c \
19 mxml-get.c \
20 mxml-index.c \
21 mxml-node.c \
22 mxml-private.c \
23 mxml-private.h \
24 mxml-search.c \
25 mxml-set.c \
26 mxml-string.c \
27 config.h \ No newline at end of file
diff --git a/pathologist/src/minixml/config.h b/pathologist/src/minixml/config.h
new file mode 100644
index 0000000..8bae4bf
--- /dev/null
+++ b/pathologist/src/minixml/config.h
@@ -0,0 +1,95 @@
1/*
2 * "$Id: config.h.in 408 2010-09-19 05:26:46Z mike $"
3 *
4 * Configuration file for Mini-XML, a small XML-like file parsing library.
5 *
6 * Copyright 2003-2010 by Michael R Sweet.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Michael R Sweet and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file. If this file is
12 * missing or damaged, see the license at:
13 *
14 * http://www.minixml.org/
15 */
16
17/*
18 * Include necessary headers...
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <stdarg.h>
25#include <ctype.h>
26
27
28/*
29 * Version number...
30 */
31
32#define MXML_VERSION ""
33
34
35/*
36 * Inline function support...
37 */
38
39#define inline
40
41
42/*
43 * Long long support...
44 */
45
46#undef HAVE_LONG_LONG
47
48
49/*
50 * Do we have the snprintf() and vsnprintf() functions?
51 */
52
53#undef HAVE_SNPRINTF
54#undef HAVE_VSNPRINTF
55
56
57/*
58 * Do we have the strXXX() functions?
59 */
60
61#undef HAVE_STRDUP
62
63
64/*
65 * Do we have threading support?
66 */
67
68#undef HAVE_PTHREAD_H
69
70
71/*
72 * Define prototypes for string functions as needed...
73 */
74
75# ifndef HAVE_STRDUP
76extern char *_mxml_strdup(const char *);
77# define strdup _mxml_strdup
78# endif /* !HAVE_STRDUP */
79
80extern char *_mxml_strdupf(const char *, ...);
81extern char *_mxml_vstrdupf(const char *, va_list);
82
83# ifndef HAVE_SNPRINTF
84extern int _mxml_snprintf(char *, size_t, const char *, ...);
85# define snprintf _mxml_snprintf
86# endif /* !HAVE_SNPRINTF */
87
88# ifndef HAVE_VSNPRINTF
89extern int _mxml_vsnprintf(char *, size_t, const char *, va_list);
90# define vsnprintf _mxml_vsnprintf
91# endif /* !HAVE_VSNPRINTF */
92
93/*
94 * End of "$Id: config.h.in 408 2010-09-19 05:26:46Z mike $".
95 */
diff --git a/pathologist/src/minixml/mxml-attr.c b/pathologist/src/minixml/mxml-attr.c
new file mode 100644
index 0000000..c9950f5
--- /dev/null
+++ b/pathologist/src/minixml/mxml-attr.c
@@ -0,0 +1,319 @@
1/*
2 * "$Id: mxml-attr.c 408 2010-09-19 05:26:46Z mike $"
3 *
4 * Attribute support code for Mini-XML, a small XML-like file parsing library.
5 *
6 * Copyright 2003-2010 by Michael R Sweet.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Michael R Sweet and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file. If this file is
12 * missing or damaged, see the license at:
13 *
14 * http://www.minixml.org/
15 *
16 * Contents:
17 *
18 * mxmlElementDeleteAttr() - Delete an attribute.
19 * mxmlElementGetAttr() - Get an attribute.
20 * mxmlElementSetAttr() - Set an attribute.
21 * mxmlElementSetAttrf() - Set an attribute with a formatted value.
22 * mxml_set_attr() - Set or add an attribute name/value pair.
23 */
24
25/*
26 * Include necessary headers...
27 */
28
29#include "config.h"
30#include "mxml.h"
31
32
33/*
34 * Local functions...
35 */
36
37static int mxml_set_attr(mxml_node_t *node, const char *name,
38 char *value);
39
40
41/*
42 * 'mxmlElementDeleteAttr()' - Delete an attribute.
43 *
44 * @since Mini-XML 2.4@
45 */
46
47void
48mxmlElementDeleteAttr(mxml_node_t *node,/* I - Element */
49 const char *name)/* I - Attribute name */
50{
51 int i; /* Looping var */
52 mxml_attr_t *attr; /* Cirrent attribute */
53
54
55#ifdef DEBUG
56 fprintf(stderr, "mxmlElementDeleteAttr(node=%p, name=\"%s\")\n",
57 node, name ? name : "(null)");
58#endif /* DEBUG */
59
60 /*
61 * Range check input...
62 */
63
64 if (!node || node->type != MXML_ELEMENT || !name)
65 return;
66
67 /*
68 * Look for the attribute...
69 */
70
71 for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
72 i > 0;
73 i --, attr ++)
74 {
75#ifdef DEBUG
76 printf(" %s=\"%s\"\n", attr->name, attr->value);
77#endif /* DEBUG */
78
79 if (!strcmp(attr->name, name))
80 {
81 /*
82 * Delete this attribute...
83 */
84
85 free(attr->name);
86 free(attr->value);
87
88 i --;
89 if (i > 0)
90 memmove(attr, attr + 1, i * sizeof(mxml_attr_t));
91
92 node->value.element.num_attrs --;
93 return;
94 }
95 }
96}
97
98
99/*
100 * 'mxmlElementGetAttr()' - Get an attribute.
101 *
102 * This function returns NULL if the node is not an element or the
103 * named attribute does not exist.
104 */
105
106const char * /* O - Attribute value or NULL */
107mxmlElementGetAttr(mxml_node_t *node, /* I - Element node */
108 const char *name) /* I - Name of attribute */
109{
110 int i; /* Looping var */
111 mxml_attr_t *attr; /* Cirrent attribute */
112
113
114#ifdef DEBUG
115 fprintf(stderr, "mxmlElementGetAttr(node=%p, name=\"%s\")\n",
116 node, name ? name : "(null)");
117#endif /* DEBUG */
118
119 /*
120 * Range check input...
121 */
122
123 if (!node || node->type != MXML_ELEMENT || !name)
124 return (NULL);
125
126 /*
127 * Look for the attribute...
128 */
129
130 for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
131 i > 0;
132 i --, attr ++)
133 {
134#ifdef DEBUG
135 printf(" %s=\"%s\"\n", attr->name, attr->value);
136#endif /* DEBUG */
137
138 if (!strcmp(attr->name, name))
139 {
140#ifdef DEBUG
141 printf(" Returning \"%s\"!\n", attr->value);
142#endif /* DEBUG */
143 return (attr->value);
144 }
145 }
146
147 /*
148 * Didn't find attribute, so return NULL...
149 */
150
151#ifdef DEBUG
152 puts(" Returning NULL!\n");
153#endif /* DEBUG */
154
155 return (NULL);
156}
157
158
159/*
160 * 'mxmlElementSetAttr()' - Set an attribute.
161 *
162 * If the named attribute already exists, the value of the attribute
163 * is replaced by the new string value. The string value is copied
164 * into the element node. This function does nothing if the node is
165 * not an element.
166 */
167
168void
169mxmlElementSetAttr(mxml_node_t *node, /* I - Element node */
170 const char *name, /* I - Name of attribute */
171 const char *value) /* I - Attribute value */
172{
173 char *valuec; /* Copy of value */
174
175
176#ifdef DEBUG
177 fprintf(stderr, "mxmlElementSetAttr(node=%p, name=\"%s\", value=\"%s\")\n",
178 node, name ? name : "(null)", value ? value : "(null)");
179#endif /* DEBUG */
180
181 /*
182 * Range check input...
183 */
184
185 if (!node || node->type != MXML_ELEMENT || !name)
186 return;
187
188 if (value)
189 valuec = strdup(value);
190 else
191 valuec = NULL;
192
193 if (mxml_set_attr(node, name, valuec))
194 free(valuec);
195}
196
197
198/*
199 * 'mxmlElementSetAttrf()' - Set an attribute with a formatted value.
200 *
201 * If the named attribute already exists, the value of the attribute
202 * is replaced by the new formatted string. The formatted string value is
203 * copied into the element node. This function does nothing if the node
204 * is not an element.
205 *
206 * @since Mini-XML 2.3@
207 */
208
209void
210mxmlElementSetAttrf(mxml_node_t *node, /* I - Element node */
211 const char *name, /* I - Name of attribute */
212 const char *format,/* I - Printf-style attribute value */
213 ...) /* I - Additional arguments as needed */
214{
215 va_list ap; /* Argument pointer */
216 char *value; /* Value */
217
218
219#ifdef DEBUG
220 fprintf(stderr,
221 "mxmlElementSetAttrf(node=%p, name=\"%s\", format=\"%s\", ...)\n",
222 node, name ? name : "(null)", format ? format : "(null)");
223#endif /* DEBUG */
224
225 /*
226 * Range check input...
227 */
228
229 if (!node || node->type != MXML_ELEMENT || !name || !format)
230 return;
231
232 /*
233 * Format the value...
234 */
235
236 va_start(ap, format);
237 value = _mxml_vstrdupf(format, ap);
238 va_end(ap);
239
240 if (!value)
241 mxml_error("Unable to allocate memory for attribute '%s' in element %s!",
242 name, node->value.element.name);
243 else if (mxml_set_attr(node, name, value))
244 free(value);
245}
246
247
248/*
249 * 'mxml_set_attr()' - Set or add an attribute name/value pair.
250 */
251
252static int /* O - 0 on success, -1 on failure */
253mxml_set_attr(mxml_node_t *node, /* I - Element node */
254 const char *name, /* I - Attribute name */
255 char *value) /* I - Attribute value */
256{
257 int i; /* Looping var */
258 mxml_attr_t *attr; /* New attribute */
259
260
261 /*
262 * Look for the attribute...
263 */
264
265 for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
266 i > 0;
267 i --, attr ++)
268 if (!strcmp(attr->name, name))
269 {
270 /*
271 * Free the old value as needed...
272 */
273
274 if (attr->value)
275 free(attr->value);
276
277 attr->value = value;
278
279 return (0);
280 }
281
282 /*
283 * Add a new attribute...
284 */
285
286 if (node->value.element.num_attrs == 0)
287 attr = malloc(sizeof(mxml_attr_t));
288 else
289 attr = realloc(node->value.element.attrs,
290 (node->value.element.num_attrs + 1) * sizeof(mxml_attr_t));
291
292 if (!attr)
293 {
294 mxml_error("Unable to allocate memory for attribute '%s' in element %s!",
295 name, node->value.element.name);
296 return (-1);
297 }
298
299 node->value.element.attrs = attr;
300 attr += node->value.element.num_attrs;
301
302 if ((attr->name = strdup(name)) == NULL)
303 {
304 mxml_error("Unable to allocate memory for attribute '%s' in element %s!",
305 name, node->value.element.name);
306 return (-1);
307 }
308
309 attr->value = value;
310
311 node->value.element.num_attrs ++;
312
313 return (0);
314}
315
316
317/*
318 * End of "$Id: mxml-attr.c 408 2010-09-19 05:26:46Z mike $".
319 */
diff --git a/pathologist/src/minixml/mxml-entity.c b/pathologist/src/minixml/mxml-entity.c
new file mode 100644
index 0000000..c5c9f61
--- /dev/null
+++ b/pathologist/src/minixml/mxml-entity.c
@@ -0,0 +1,460 @@
1/*
2 * "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $"
3 *
4 * Character entity support code for Mini-XML, a small XML-like
5 * file parsing library.
6 *
7 * Copyright 2003-2010 by Michael R Sweet.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Michael R Sweet and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "COPYING"
12 * which should have been included with this file. If this file is
13 * missing or damaged, see the license at:
14 *
15 * http://www.minixml.org/
16 *
17 * Contents:
18 *
19 * mxmlEntityAddCallback() - Add a callback to convert entities to
20 * Unicode.
21 * mxmlEntityGetName() - Get the name that corresponds to the
22 * character value.
23 * mxmlEntityGetValue() - Get the character corresponding to a named
24 * entity.
25 * mxmlEntityRemoveCallback() - Remove a callback.
26 * _mxml_entity_cb() - Lookup standard (X)HTML entities.
27 */
28
29/*
30 * Include necessary headers...
31 */
32
33#include "mxml-private.h"
34
35
36/*
37 * 'mxmlEntityAddCallback()' - Add a callback to convert entities to Unicode.
38 */
39
40int /* O - 0 on success, -1 on failure */
41mxmlEntityAddCallback(
42 mxml_entity_cb_t cb) /* I - Callback function to add */
43{
44 _mxml_global_t *global = _mxml_global();
45 /* Global data */
46
47
48 if (global->num_entity_cbs < (int)(sizeof(global->entity_cbs) / sizeof(global->entity_cbs[0])))
49 {
50 global->entity_cbs[global->num_entity_cbs] = cb;
51 global->num_entity_cbs ++;
52
53 return (0);
54 }
55 else
56 {
57 mxml_error("Unable to add entity callback!");
58
59 return (-1);
60 }
61}
62
63
64/*
65 * 'mxmlEntityGetName()' - Get the name that corresponds to the character value.
66 *
67 * If val does not need to be represented by a named entity, NULL is returned.
68 */
69
70const char * /* O - Entity name or NULL */
71mxmlEntityGetName(int val) /* I - Character value */
72{
73 switch (val)
74 {
75 case '&' :
76 return ("amp");
77
78 case '<' :
79 return ("lt");
80
81 case '>' :
82 return ("gt");
83
84 case '\"' :
85 return ("quot");
86
87 default :
88 return (NULL);
89 }
90}
91
92
93/*
94 * 'mxmlEntityGetValue()' - Get the character corresponding to a named entity.
95 *
96 * The entity name can also be a numeric constant. -1 is returned if the
97 * name is not known.
98 */
99
100int /* O - Character value or -1 on error */
101mxmlEntityGetValue(const char *name) /* I - Entity name */
102{
103 int i; /* Looping var */
104 int ch; /* Character value */
105 _mxml_global_t *global = _mxml_global();
106 /* Global data */
107
108
109 for (i = 0; i < global->num_entity_cbs; i ++)
110 if ((ch = (global->entity_cbs[i])(name)) >= 0)
111 return (ch);
112
113 return (-1);
114}
115
116
117/*
118 * 'mxmlEntityRemoveCallback()' - Remove a callback.
119 */
120
121void
122mxmlEntityRemoveCallback(
123 mxml_entity_cb_t cb) /* I - Callback function to remove */
124{
125 int i; /* Looping var */
126 _mxml_global_t *global = _mxml_global();
127 /* Global data */
128
129
130 for (i = 0; i < global->num_entity_cbs; i ++)
131 if (cb == global->entity_cbs[i])
132 {
133 /*
134 * Remove the callback...
135 */
136
137 global->num_entity_cbs --;
138
139 if (i < global->num_entity_cbs)
140 memmove(global->entity_cbs + i, global->entity_cbs + i + 1,
141 (global->num_entity_cbs - i) * sizeof(global->entity_cbs[0]));
142
143 return;
144 }
145}
146
147
148/*
149 * '_mxml_entity_cb()' - Lookup standard (X)HTML entities.
150 */
151
152int /* O - Unicode value or -1 */
153_mxml_entity_cb(const char *name) /* I - Entity name */
154{
155 int diff, /* Difference between names */
156 current, /* Current entity in search */
157 first, /* First entity in search */
158 last; /* Last entity in search */
159 static const struct
160 {
161 const char *name; /* Entity name */
162 int val; /* Character value */
163 } entities[] =
164 {
165 { "AElig", 198 },
166 { "Aacute", 193 },
167 { "Acirc", 194 },
168 { "Agrave", 192 },
169 { "Alpha", 913 },
170 { "Aring", 197 },
171 { "Atilde", 195 },
172 { "Auml", 196 },
173 { "Beta", 914 },
174 { "Ccedil", 199 },
175 { "Chi", 935 },
176 { "Dagger", 8225 },
177 { "Delta", 916 },
178 { "Dstrok", 208 },
179 { "ETH", 208 },
180 { "Eacute", 201 },
181 { "Ecirc", 202 },
182 { "Egrave", 200 },
183 { "Epsilon", 917 },
184 { "Eta", 919 },
185 { "Euml", 203 },
186 { "Gamma", 915 },
187 { "Iacute", 205 },
188 { "Icirc", 206 },
189 { "Igrave", 204 },
190 { "Iota", 921 },
191 { "Iuml", 207 },
192 { "Kappa", 922 },
193 { "Lambda", 923 },
194 { "Mu", 924 },
195 { "Ntilde", 209 },
196 { "Nu", 925 },
197 { "OElig", 338 },
198 { "Oacute", 211 },
199 { "Ocirc", 212 },
200 { "Ograve", 210 },
201 { "Omega", 937 },
202 { "Omicron", 927 },
203 { "Oslash", 216 },
204 { "Otilde", 213 },
205 { "Ouml", 214 },
206 { "Phi", 934 },
207 { "Pi", 928 },
208 { "Prime", 8243 },
209 { "Psi", 936 },
210 { "Rho", 929 },
211 { "Scaron", 352 },
212 { "Sigma", 931 },
213 { "THORN", 222 },
214 { "Tau", 932 },
215 { "Theta", 920 },
216 { "Uacute", 218 },
217 { "Ucirc", 219 },
218 { "Ugrave", 217 },
219 { "Upsilon", 933 },
220 { "Uuml", 220 },
221 { "Xi", 926 },
222 { "Yacute", 221 },
223 { "Yuml", 376 },
224 { "Zeta", 918 },
225 { "aacute", 225 },
226 { "acirc", 226 },
227 { "acute", 180 },
228 { "aelig", 230 },
229 { "agrave", 224 },
230 { "alefsym", 8501 },
231 { "alpha", 945 },
232 { "amp", '&' },
233 { "and", 8743 },
234 { "ang", 8736 },
235 { "apos", '\'' },
236 { "aring", 229 },
237 { "asymp", 8776 },
238 { "atilde", 227 },
239 { "auml", 228 },
240 { "bdquo", 8222 },
241 { "beta", 946 },
242 { "brkbar", 166 },
243 { "brvbar", 166 },
244 { "bull", 8226 },
245 { "cap", 8745 },
246 { "ccedil", 231 },
247 { "cedil", 184 },
248 { "cent", 162 },
249 { "chi", 967 },
250 { "circ", 710 },
251 { "clubs", 9827 },
252 { "cong", 8773 },
253 { "copy", 169 },
254 { "crarr", 8629 },
255 { "cup", 8746 },
256 { "curren", 164 },
257 { "dArr", 8659 },
258 { "dagger", 8224 },
259 { "darr", 8595 },
260 { "deg", 176 },
261 { "delta", 948 },
262 { "diams", 9830 },
263 { "die", 168 },
264 { "divide", 247 },
265 { "eacute", 233 },
266 { "ecirc", 234 },
267 { "egrave", 232 },
268 { "empty", 8709 },
269 { "emsp", 8195 },
270 { "ensp", 8194 },
271 { "epsilon", 949 },
272 { "equiv", 8801 },
273 { "eta", 951 },
274 { "eth", 240 },
275 { "euml", 235 },
276 { "euro", 8364 },
277 { "exist", 8707 },
278 { "fnof", 402 },
279 { "forall", 8704 },
280 { "frac12", 189 },
281 { "frac14", 188 },
282 { "frac34", 190 },
283 { "frasl", 8260 },
284 { "gamma", 947 },
285 { "ge", 8805 },
286 { "gt", '>' },
287 { "hArr", 8660 },
288 { "harr", 8596 },
289 { "hearts", 9829 },
290 { "hellip", 8230 },
291 { "hibar", 175 },
292 { "iacute", 237 },
293 { "icirc", 238 },
294 { "iexcl", 161 },
295 { "igrave", 236 },
296 { "image", 8465 },
297 { "infin", 8734 },
298 { "int", 8747 },
299 { "iota", 953 },
300 { "iquest", 191 },
301 { "isin", 8712 },
302 { "iuml", 239 },
303 { "kappa", 954 },
304 { "lArr", 8656 },
305 { "lambda", 955 },
306 { "lang", 9001 },
307 { "laquo", 171 },
308 { "larr", 8592 },
309 { "lceil", 8968 },
310 { "ldquo", 8220 },
311 { "le", 8804 },
312 { "lfloor", 8970 },
313 { "lowast", 8727 },
314 { "loz", 9674 },
315 { "lrm", 8206 },
316 { "lsaquo", 8249 },
317 { "lsquo", 8216 },
318 { "lt", '<' },
319 { "macr", 175 },
320 { "mdash", 8212 },
321 { "micro", 181 },
322 { "middot", 183 },
323 { "minus", 8722 },
324 { "mu", 956 },
325 { "nabla", 8711 },
326 { "nbsp", 160 },
327 { "ndash", 8211 },
328 { "ne", 8800 },
329 { "ni", 8715 },
330 { "not", 172 },
331 { "notin", 8713 },
332 { "nsub", 8836 },
333 { "ntilde", 241 },
334 { "nu", 957 },
335 { "oacute", 243 },
336 { "ocirc", 244 },
337 { "oelig", 339 },
338 { "ograve", 242 },
339 { "oline", 8254 },
340 { "omega", 969 },
341 { "omicron", 959 },
342 { "oplus", 8853 },
343 { "or", 8744 },
344 { "ordf", 170 },
345 { "ordm", 186 },
346 { "oslash", 248 },
347 { "otilde", 245 },
348 { "otimes", 8855 },
349 { "ouml", 246 },
350 { "para", 182 },
351 { "part", 8706 },
352 { "permil", 8240 },
353 { "perp", 8869 },
354 { "phi", 966 },
355 { "pi", 960 },
356 { "piv", 982 },
357 { "plusmn", 177 },
358 { "pound", 163 },
359 { "prime", 8242 },
360 { "prod", 8719 },
361 { "prop", 8733 },
362 { "psi", 968 },
363 { "quot", '\"' },
364 { "rArr", 8658 },
365 { "radic", 8730 },
366 { "rang", 9002 },
367 { "raquo", 187 },
368 { "rarr", 8594 },
369 { "rceil", 8969 },
370 { "rdquo", 8221 },
371 { "real", 8476 },
372 { "reg", 174 },
373 { "rfloor", 8971 },
374 { "rho", 961 },
375 { "rlm", 8207 },
376 { "rsaquo", 8250 },
377 { "rsquo", 8217 },
378 { "sbquo", 8218 },
379 { "scaron", 353 },
380 { "sdot", 8901 },
381 { "sect", 167 },
382 { "shy", 173 },
383 { "sigma", 963 },
384 { "sigmaf", 962 },
385 { "sim", 8764 },
386 { "spades", 9824 },
387 { "sub", 8834 },
388 { "sube", 8838 },
389 { "sum", 8721 },
390 { "sup", 8835 },
391 { "sup1", 185 },
392 { "sup2", 178 },
393 { "sup3", 179 },
394 { "supe", 8839 },
395 { "szlig", 223 },
396 { "tau", 964 },
397 { "there4", 8756 },
398 { "theta", 952 },
399 { "thetasym", 977 },
400 { "thinsp", 8201 },
401 { "thorn", 254 },
402 { "tilde", 732 },
403 { "times", 215 },
404 { "trade", 8482 },
405 { "uArr", 8657 },
406 { "uacute", 250 },
407 { "uarr", 8593 },
408 { "ucirc", 251 },
409 { "ugrave", 249 },
410 { "uml", 168 },
411 { "upsih", 978 },
412 { "upsilon", 965 },
413 { "uuml", 252 },
414 { "weierp", 8472 },
415 { "xi", 958 },
416 { "yacute", 253 },
417 { "yen", 165 },
418 { "yuml", 255 },
419 { "zeta", 950 },
420 { "zwj", 8205 },
421 { "zwnj", 8204 }
422 };
423
424
425 /*
426 * Do a binary search for the named entity...
427 */
428
429 first = 0;
430 last = (int)(sizeof(entities) / sizeof(entities[0]) - 1);
431
432 while ((last - first) > 1)
433 {
434 current = (first + last) / 2;
435
436 if ((diff = strcmp(name, entities[current].name)) == 0)
437 return (entities[current].val);
438 else if (diff < 0)
439 last = current;
440 else
441 first = current;
442 }
443
444 /*
445 * If we get here, there is a small chance that there is still
446 * a match; check first and last...
447 */
448
449 if (!strcmp(name, entities[first].name))
450 return (entities[first].val);
451 else if (!strcmp(name, entities[last].name))
452 return (entities[last].val);
453 else
454 return (-1);
455}
456
457
458/*
459 * End of "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $".
460 */
diff --git a/pathologist/src/minixml/mxml-file.c b/pathologist/src/minixml/mxml-file.c
new file mode 100644
index 0000000..9927040
--- /dev/null
+++ b/pathologist/src/minixml/mxml-file.c
@@ -0,0 +1,3080 @@
1/*
2 * "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $"
3 *
4 * File loading code for Mini-XML, a small XML-like file parsing library.
5 *
6 * Copyright 2003-2011 by Michael R Sweet.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Michael R Sweet and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file. If this file is
12 * missing or damaged, see the license at:
13 *
14 * http://www.minixml.org/
15 *
16 * Contents:
17 *
18 * mxmlLoadFd() - Load a file descriptor into an XML node tree.
19 * mxmlLoadFile() - Load a file into an XML node tree.
20 * mxmlLoadString() - Load a string into an XML node tree.
21 * mxmlSaveAllocString() - Save an XML tree to an allocated string.
22 * mxmlSaveFd() - Save an XML tree to a file descriptor.
23 * mxmlSaveFile() - Save an XML tree to a file.
24 * mxmlSaveString() - Save an XML node tree to a string.
25 * mxmlSAXLoadFd() - Load a file descriptor into an XML node tree
26 * using a SAX callback.
27 * mxmlSAXLoadFile() - Load a file into an XML node tree
28 * using a SAX callback.
29 * mxmlSAXLoadString() - Load a string into an XML node tree
30 * using a SAX callback.
31 * mxmlSetCustomHandlers() - Set the handling functions for custom data.
32 * mxmlSetErrorCallback() - Set the error message callback.
33 * mxmlSetWrapMargin() - Set the wrap margin when saving XML data.
34 * mxml_add_char() - Add a character to a buffer, expanding as needed.
35 * mxml_fd_getc() - Read a character from a file descriptor.
36 * mxml_fd_putc() - Write a character to a file descriptor.
37 * mxml_fd_read() - Read a buffer of data from a file descriptor.
38 * mxml_fd_write() - Write a buffer of data to a file descriptor.
39 * mxml_file_getc() - Get a character from a file.
40 * mxml_file_putc() - Write a character to a file.
41 * mxml_get_entity() - Get the character corresponding to an entity...
42 * mxml_load_data() - Load data into an XML node tree.
43 * mxml_parse_element() - Parse an element for any attributes...
44 * mxml_string_getc() - Get a character from a string.
45 * mxml_string_putc() - Write a character to a string.
46 * mxml_write_name() - Write a name string.
47 * mxml_write_node() - Save an XML node to a file.
48 * mxml_write_string() - Write a string, escaping & and < as needed.
49 * mxml_write_ws() - Do whitespace callback...
50 */
51
52/*
53 * Include necessary headers...
54 */
55
56#ifndef WIN32
57# include <unistd.h>
58#endif /* !WIN32 */
59#include "mxml-private.h"
60
61
62/*
63 * Character encoding...
64 */
65
66#define ENCODE_UTF8 0 /* UTF-8 */
67#define ENCODE_UTF16BE 1 /* UTF-16 Big-Endian */
68#define ENCODE_UTF16LE 2 /* UTF-16 Little-Endian */
69
70
71/*
72 * Macro to test for a bad XML character...
73 */
74
75#define mxml_bad_char(ch) ((ch) < ' ' && (ch) != '\n' && (ch) != '\r' && (ch) != '\t')
76
77
78/*
79 * Types and structures...
80 */
81
82typedef int (*_mxml_getc_cb_t)(void *, int *);
83typedef int (*_mxml_putc_cb_t)(int, void *);
84
85typedef struct _mxml_fdbuf_s /**** File descriptor buffer ****/
86{
87 int fd; /* File descriptor */
88 unsigned char *current, /* Current position in buffer */
89 *end, /* End of buffer */
90 buffer[8192]; /* Character buffer */
91} _mxml_fdbuf_t;
92
93
94/*
95 * Local functions...
96 */
97
98static int mxml_add_char(int ch, char **ptr, char **buffer,
99 int *bufsize);
100static int mxml_fd_getc(void *p, int *encoding);
101static int mxml_fd_putc(int ch, void *p);
102static int mxml_fd_read(_mxml_fdbuf_t *buf);
103static int mxml_fd_write(_mxml_fdbuf_t *buf);
104static int mxml_file_getc(void *p, int *encoding);
105static int mxml_file_putc(int ch, void *p);
106static int mxml_get_entity(mxml_node_t *parent, void *p,
107 int *encoding,
108 _mxml_getc_cb_t getc_cb);
109static inline int mxml_isspace(int ch)
110 {
111 return (ch == ' ' || ch == '\t' || ch == '\r' ||
112 ch == '\n');
113 }
114static mxml_node_t *mxml_load_data(mxml_node_t *top, void *p,
115 mxml_load_cb_t cb,
116 _mxml_getc_cb_t getc_cb,
117 mxml_sax_cb_t sax_cb, void *sax_data);
118static int mxml_parse_element(mxml_node_t *node, void *p,
119 int *encoding,
120 _mxml_getc_cb_t getc_cb);
121static int mxml_string_getc(void *p, int *encoding);
122static int mxml_string_putc(int ch, void *p);
123static int mxml_write_name(const char *s, void *p,
124 _mxml_putc_cb_t putc_cb);
125static int mxml_write_node(mxml_node_t *node, void *p,
126 mxml_save_cb_t cb, int col,
127 _mxml_putc_cb_t putc_cb,
128 _mxml_global_t *global);
129static int mxml_write_string(const char *s, void *p,
130 _mxml_putc_cb_t putc_cb);
131static int mxml_write_ws(mxml_node_t *node, void *p,
132 mxml_save_cb_t cb, int ws,
133 int col, _mxml_putc_cb_t putc_cb);
134
135
136/*
137 * 'mxmlLoadFd()' - Load a file descriptor into an XML node tree.
138 *
139 * The nodes in the specified file are added to the specified top node.
140 * If no top node is provided, the XML file MUST be well-formed with a
141 * single parent node like <?xml> for the entire file. The callback
142 * function returns the value type that should be used for child nodes.
143 * If MXML_NO_CALLBACK is specified then all child nodes will be either
144 * MXML_ELEMENT or MXML_TEXT nodes.
145 *
146 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
147 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
148 * child nodes of the specified type.
149 */
150
151mxml_node_t * /* O - First node or NULL if the file could not be read. */
152mxmlLoadFd(mxml_node_t *top, /* I - Top node */
153 int fd, /* I - File descriptor to read from */
154 mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */
155{
156 _mxml_fdbuf_t buf; /* File descriptor buffer */
157
158
159 /*
160 * Initialize the file descriptor buffer...
161 */
162
163 buf.fd = fd;
164 buf.current = buf.buffer;
165 buf.end = buf.buffer;
166
167 /*
168 * Read the XML data...
169 */
170
171 return (mxml_load_data(top, &buf, cb, mxml_fd_getc, MXML_NO_CALLBACK, NULL));
172}
173
174
175/*
176 * 'mxmlLoadFile()' - Load a file into an XML node tree.
177 *
178 * The nodes in the specified file are added to the specified top node.
179 * If no top node is provided, the XML file MUST be well-formed with a
180 * single parent node like <?xml> for the entire file. The callback
181 * function returns the value type that should be used for child nodes.
182 * If MXML_NO_CALLBACK is specified then all child nodes will be either
183 * MXML_ELEMENT or MXML_TEXT nodes.
184 *
185 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
186 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
187 * child nodes of the specified type.
188 */
189
190mxml_node_t * /* O - First node or NULL if the file could not be read. */
191mxmlLoadFile(mxml_node_t *top, /* I - Top node */
192 FILE *fp, /* I - File to read from */
193 mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */
194{
195 /*
196 * Read the XML data...
197 */
198
199 return (mxml_load_data(top, fp, cb, mxml_file_getc, MXML_NO_CALLBACK, NULL));
200}
201
202
203/*
204 * 'mxmlLoadString()' - Load a string into an XML node tree.
205 *
206 * The nodes in the specified string are added to the specified top node.
207 * If no top node is provided, the XML string MUST be well-formed with a
208 * single parent node like <?xml> for the entire string. The callback
209 * function returns the value type that should be used for child nodes.
210 * If MXML_NO_CALLBACK is specified then all child nodes will be either
211 * MXML_ELEMENT or MXML_TEXT nodes.
212 *
213 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
214 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
215 * child nodes of the specified type.
216 */
217
218mxml_node_t * /* O - First node or NULL if the string has errors. */
219mxmlLoadString(mxml_node_t *top, /* I - Top node */
220 const char *s, /* I - String to load */
221 mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */
222{
223 /*
224 * Read the XML data...
225 */
226
227 return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, MXML_NO_CALLBACK,
228 NULL));
229}
230
231
232/*
233 * 'mxmlSaveAllocString()' - Save an XML tree to an allocated string.
234 *
235 * This function returns a pointer to a string containing the textual
236 * representation of the XML node tree. The string should be freed
237 * using the free() function when you are done with it. NULL is returned
238 * if the node would produce an empty string or if the string cannot be
239 * allocated.
240 *
241 * The callback argument specifies a function that returns a whitespace
242 * string or NULL before and after each element. If MXML_NO_CALLBACK
243 * is specified, whitespace will only be added before MXML_TEXT nodes
244 * with leading whitespace and before attribute names inside opening
245 * element tags.
246 */
247
248char * /* O - Allocated string or NULL */
249mxmlSaveAllocString(
250 mxml_node_t *node, /* I - Node to write */
251 mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
252{
253 int bytes; /* Required bytes */
254 char buffer[8192]; /* Temporary buffer */
255 char *s; /* Allocated string */
256
257
258 /*
259 * Write the node to the temporary buffer...
260 */
261
262 bytes = mxmlSaveString(node, buffer, sizeof(buffer), cb);
263
264 if (bytes <= 0)
265 return (NULL);
266
267 if (bytes < (int)(sizeof(buffer) - 1))
268 {
269 /*
270 * Node fit inside the buffer, so just duplicate that string and
271 * return...
272 */
273
274 return (strdup(buffer));
275 }
276
277 /*
278 * Allocate a buffer of the required size and save the node to the
279 * new buffer...
280 */
281
282 if ((s = malloc(bytes + 1)) == NULL)
283 return (NULL);
284
285 mxmlSaveString(node, s, bytes + 1, cb);
286
287 /*
288 * Return the allocated string...
289 */
290
291 return (s);
292}
293
294
295/*
296 * 'mxmlSaveFd()' - Save an XML tree to a file descriptor.
297 *
298 * The callback argument specifies a function that returns a whitespace
299 * string or NULL before and after each element. If MXML_NO_CALLBACK
300 * is specified, whitespace will only be added before MXML_TEXT nodes
301 * with leading whitespace and before attribute names inside opening
302 * element tags.
303 */
304
305int /* O - 0 on success, -1 on error. */
306mxmlSaveFd(mxml_node_t *node, /* I - Node to write */
307 int fd, /* I - File descriptor to write to */
308 mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
309{
310 int col; /* Final column */
311 _mxml_fdbuf_t buf; /* File descriptor buffer */
312 _mxml_global_t *global = _mxml_global();
313 /* Global data */
314
315
316 /*
317 * Initialize the file descriptor buffer...
318 */
319
320 buf.fd = fd;
321 buf.current = buf.buffer;
322 buf.end = buf.buffer + sizeof(buf.buffer);
323
324 /*
325 * Write the node...
326 */
327
328 if ((col = mxml_write_node(node, &buf, cb, 0, mxml_fd_putc, global)) < 0)
329 return (-1);
330
331 if (col > 0)
332 if (mxml_fd_putc('\n', &buf) < 0)
333 return (-1);
334
335 /*
336 * Flush and return...
337 */
338
339 return (mxml_fd_write(&buf));
340}
341
342
343/*
344 * 'mxmlSaveFile()' - Save an XML tree to a file.
345 *
346 * The callback argument specifies a function that returns a whitespace
347 * string or NULL before and after each element. If MXML_NO_CALLBACK
348 * is specified, whitespace will only be added before MXML_TEXT nodes
349 * with leading whitespace and before attribute names inside opening
350 * element tags.
351 */
352
353int /* O - 0 on success, -1 on error. */
354mxmlSaveFile(mxml_node_t *node, /* I - Node to write */
355 FILE *fp, /* I - File to write to */
356 mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
357{
358 int col; /* Final column */
359 _mxml_global_t *global = _mxml_global();
360 /* Global data */
361
362
363 /*
364 * Write the node...
365 */
366
367 if ((col = mxml_write_node(node, fp, cb, 0, mxml_file_putc, global)) < 0)
368 return (-1);
369
370 if (col > 0)
371 if (putc('\n', fp) < 0)
372 return (-1);
373
374 /*
375 * Return 0 (success)...
376 */
377
378 return (0);
379}
380
381
382/*
383 * 'mxmlSaveString()' - Save an XML node tree to a string.
384 *
385 * This function returns the total number of bytes that would be
386 * required for the string but only copies (bufsize - 1) characters
387 * into the specified buffer.
388 *
389 * The callback argument specifies a function that returns a whitespace
390 * string or NULL before and after each element. If MXML_NO_CALLBACK
391 * is specified, whitespace will only be added before MXML_TEXT nodes
392 * with leading whitespace and before attribute names inside opening
393 * element tags.
394 */
395
396int /* O - Size of string */
397mxmlSaveString(mxml_node_t *node, /* I - Node to write */
398 char *buffer, /* I - String buffer */
399 int bufsize, /* I - Size of string buffer */
400 mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
401{
402 int col; /* Final column */
403 char *ptr[2]; /* Pointers for putc_cb */
404 _mxml_global_t *global = _mxml_global();
405 /* Global data */
406
407
408 /*
409 * Write the node...
410 */
411
412 ptr[0] = buffer;
413 ptr[1] = buffer + bufsize;
414
415 if ((col = mxml_write_node(node, ptr, cb, 0, mxml_string_putc, global)) < 0)
416 return (-1);
417
418 if (col > 0)
419 mxml_string_putc('\n', ptr);
420
421 /*
422 * Nul-terminate the buffer...
423 */
424
425 if (ptr[0] >= ptr[1])
426 buffer[bufsize - 1] = '\0';
427 else
428 ptr[0][0] = '\0';
429
430 /*
431 * Return the number of characters...
432 */
433
434 return (ptr[0] - buffer);
435}
436
437
438/*
439 * 'mxmlSAXLoadFd()' - Load a file descriptor into an XML node tree
440 * using a SAX callback.
441 *
442 * The nodes in the specified file are added to the specified top node.
443 * If no top node is provided, the XML file MUST be well-formed with a
444 * single parent node like <?xml> for the entire file. The callback
445 * function returns the value type that should be used for child nodes.
446 * If MXML_NO_CALLBACK is specified then all child nodes will be either
447 * MXML_ELEMENT or MXML_TEXT nodes.
448 *
449 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
450 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
451 * child nodes of the specified type.
452 *
453 * The SAX callback must call mxmlRetain() for any nodes that need to
454 * be kept for later use. Otherwise, nodes are deleted when the parent
455 * node is closed or after each data, comment, CDATA, or directive node.
456 *
457 * @since Mini-XML 2.3@
458 */
459
460mxml_node_t * /* O - First node or NULL if the file could not be read. */
461mxmlSAXLoadFd(mxml_node_t *top, /* I - Top node */
462 int fd, /* I - File descriptor to read from */
463 mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
464 mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
465 void *sax_data) /* I - SAX user data */
466{
467 _mxml_fdbuf_t buf; /* File descriptor buffer */
468
469
470 /*
471 * Initialize the file descriptor buffer...
472 */
473
474 buf.fd = fd;
475 buf.current = buf.buffer;
476 buf.end = buf.buffer;
477
478 /*
479 * Read the XML data...
480 */
481
482 return (mxml_load_data(top, &buf, cb, mxml_fd_getc, sax_cb, sax_data));
483}
484
485
486/*
487 * 'mxmlSAXLoadFile()' - Load a file into an XML node tree
488 * using a SAX callback.
489 *
490 * The nodes in the specified file are added to the specified top node.
491 * If no top node is provided, the XML file MUST be well-formed with a
492 * single parent node like <?xml> for the entire file. The callback
493 * function returns the value type that should be used for child nodes.
494 * If MXML_NO_CALLBACK is specified then all child nodes will be either
495 * MXML_ELEMENT or MXML_TEXT nodes.
496 *
497 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
498 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
499 * child nodes of the specified type.
500 *
501 * The SAX callback must call mxmlRetain() for any nodes that need to
502 * be kept for later use. Otherwise, nodes are deleted when the parent
503 * node is closed or after each data, comment, CDATA, or directive node.
504 *
505 * @since Mini-XML 2.3@
506 */
507
508mxml_node_t * /* O - First node or NULL if the file could not be read. */
509mxmlSAXLoadFile(
510 mxml_node_t *top, /* I - Top node */
511 FILE *fp, /* I - File to read from */
512 mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
513 mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
514 void *sax_data) /* I - SAX user data */
515{
516 /*
517 * Read the XML data...
518 */
519
520 return (mxml_load_data(top, fp, cb, mxml_file_getc, sax_cb, sax_data));
521}
522
523
524/*
525 * 'mxmlSAXLoadString()' - Load a string into an XML node tree
526 * using a SAX callback.
527 *
528 * The nodes in the specified string are added to the specified top node.
529 * If no top node is provided, the XML string MUST be well-formed with a
530 * single parent node like <?xml> for the entire string. The callback
531 * function returns the value type that should be used for child nodes.
532 * If MXML_NO_CALLBACK is specified then all child nodes will be either
533 * MXML_ELEMENT or MXML_TEXT nodes.
534 *
535 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
536 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
537 * child nodes of the specified type.
538 *
539 * The SAX callback must call mxmlRetain() for any nodes that need to
540 * be kept for later use. Otherwise, nodes are deleted when the parent
541 * node is closed or after each data, comment, CDATA, or directive node.
542 *
543 * @since Mini-XML 2.3@
544 */
545
546mxml_node_t * /* O - First node or NULL if the string has errors. */
547mxmlSAXLoadString(
548 mxml_node_t *top, /* I - Top node */
549 const char *s, /* I - String to load */
550 mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
551 mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
552 void *sax_data) /* I - SAX user data */
553{
554 /*
555 * Read the XML data...
556 */
557
558 return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, sax_cb, sax_data));
559}
560
561
562/*
563 * 'mxmlSetCustomHandlers()' - Set the handling functions for custom data.
564 *
565 * The load function accepts a node pointer and a data string and must
566 * return 0 on success and non-zero on error.
567 *
568 * The save function accepts a node pointer and must return a malloc'd
569 * string on success and NULL on error.
570 *
571 */
572
573void
574mxmlSetCustomHandlers(
575 mxml_custom_load_cb_t load, /* I - Load function */
576 mxml_custom_save_cb_t save) /* I - Save function */
577{
578 _mxml_global_t *global = _mxml_global();
579 /* Global data */
580
581
582 global->custom_load_cb = load;
583 global->custom_save_cb = save;
584}
585
586
587/*
588 * 'mxmlSetErrorCallback()' - Set the error message callback.
589 */
590
591void
592mxmlSetErrorCallback(mxml_error_cb_t cb)/* I - Error callback function */
593{
594 _mxml_global_t *global = _mxml_global();
595 /* Global data */
596
597
598 global->error_cb = cb;
599}
600
601
602/*
603 * 'mxmlSetWrapMargin()' - Set the wrap margin when saving XML data.
604 *
605 * Wrapping is disabled when "column" is 0.
606 *
607 * @since Mini-XML 2.3@
608 */
609
610void
611mxmlSetWrapMargin(int column) /* I - Column for wrapping, 0 to disable wrapping */
612{
613 _mxml_global_t *global = _mxml_global();
614 /* Global data */
615
616
617 global->wrap = column;
618}
619
620
621/*
622 * 'mxml_add_char()' - Add a character to a buffer, expanding as needed.
623 */
624
625static int /* O - 0 on success, -1 on error */
626mxml_add_char(int ch, /* I - Character to add */
627 char **bufptr, /* IO - Current position in buffer */
628 char **buffer, /* IO - Current buffer */
629 int *bufsize) /* IO - Current buffer size */
630{
631 char *newbuffer; /* New buffer value */
632
633
634 if (*bufptr >= (*buffer + *bufsize - 4))
635 {
636 /*
637 * Increase the size of the buffer...
638 */
639
640 if (*bufsize < 1024)
641 (*bufsize) *= 2;
642 else
643 (*bufsize) += 1024;
644
645 if ((newbuffer = realloc(*buffer, *bufsize)) == NULL)
646 {
647 free(*buffer);
648
649 mxml_error("Unable to expand string buffer to %d bytes!", *bufsize);
650
651 return (-1);
652 }
653
654 *bufptr = newbuffer + (*bufptr - *buffer);
655 *buffer = newbuffer;
656 }
657
658 if (ch < 0x80)
659 {
660 /*
661 * Single byte ASCII...
662 */
663
664 *(*bufptr)++ = ch;
665 }
666 else if (ch < 0x800)
667 {
668 /*
669 * Two-byte UTF-8...
670 */
671
672 *(*bufptr)++ = 0xc0 | (ch >> 6);
673 *(*bufptr)++ = 0x80 | (ch & 0x3f);
674 }
675 else if (ch < 0x10000)
676 {
677 /*
678 * Three-byte UTF-8...
679 */
680
681 *(*bufptr)++ = 0xe0 | (ch >> 12);
682 *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f);
683 *(*bufptr)++ = 0x80 | (ch & 0x3f);
684 }
685 else
686 {
687 /*
688 * Four-byte UTF-8...
689 */
690
691 *(*bufptr)++ = 0xf0 | (ch >> 18);
692 *(*bufptr)++ = 0x80 | ((ch >> 12) & 0x3f);
693 *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f);
694 *(*bufptr)++ = 0x80 | (ch & 0x3f);
695 }
696
697 return (0);
698}
699
700
701/*
702 * 'mxml_fd_getc()' - Read a character from a file descriptor.
703 */
704
705static int /* O - Character or EOF */
706mxml_fd_getc(void *p, /* I - File descriptor buffer */
707 int *encoding) /* IO - Encoding */
708{
709 _mxml_fdbuf_t *buf; /* File descriptor buffer */
710 int ch, /* Current character */
711 temp; /* Temporary character */
712
713
714 /*
715 * Grab the next character in the buffer...
716 */
717
718 buf = (_mxml_fdbuf_t *)p;
719
720 if (buf->current >= buf->end)
721 if (mxml_fd_read(buf) < 0)
722 return (EOF);
723
724 ch = *(buf->current)++;
725
726 switch (*encoding)
727 {
728 case ENCODE_UTF8 :
729 /*
730 * Got a UTF-8 character; convert UTF-8 to Unicode and return...
731 */
732
733 if (!(ch & 0x80))
734 {
735#if DEBUG > 1
736 printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
737#endif /* DEBUG > 1 */
738
739 if (mxml_bad_char(ch))
740 {
741 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
742 ch);
743 return (EOF);
744 }
745
746 return (ch);
747 }
748 else if (ch == 0xfe)
749 {
750 /*
751 * UTF-16 big-endian BOM?
752 */
753
754 if (buf->current >= buf->end)
755 if (mxml_fd_read(buf) < 0)
756 return (EOF);
757
758 ch = *(buf->current)++;
759
760 if (ch != 0xff)
761 return (EOF);
762
763 *encoding = ENCODE_UTF16BE;
764
765 return (mxml_fd_getc(p, encoding));
766 }
767 else if (ch == 0xff)
768 {
769 /*
770 * UTF-16 little-endian BOM?
771 */
772
773 if (buf->current >= buf->end)
774 if (mxml_fd_read(buf) < 0)
775 return (EOF);
776
777 ch = *(buf->current)++;
778
779 if (ch != 0xfe)
780 return (EOF);
781
782 *encoding = ENCODE_UTF16LE;
783
784 return (mxml_fd_getc(p, encoding));
785 }
786 else if ((ch & 0xe0) == 0xc0)
787 {
788 /*
789 * Two-byte value...
790 */
791
792 if (buf->current >= buf->end)
793 if (mxml_fd_read(buf) < 0)
794 return (EOF);
795
796 temp = *(buf->current)++;
797
798 if ((temp & 0xc0) != 0x80)
799 return (EOF);
800
801 ch = ((ch & 0x1f) << 6) | (temp & 0x3f);
802
803 if (ch < 0x80)
804 {
805 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
806 return (EOF);
807 }
808 }
809 else if ((ch & 0xf0) == 0xe0)
810 {
811 /*
812 * Three-byte value...
813 */
814
815 if (buf->current >= buf->end)
816 if (mxml_fd_read(buf) < 0)
817 return (EOF);
818
819 temp = *(buf->current)++;
820
821 if ((temp & 0xc0) != 0x80)
822 return (EOF);
823
824 ch = ((ch & 0x0f) << 6) | (temp & 0x3f);
825
826 if (buf->current >= buf->end)
827 if (mxml_fd_read(buf) < 0)
828 return (EOF);
829
830 temp = *(buf->current)++;
831
832 if ((temp & 0xc0) != 0x80)
833 return (EOF);
834
835 ch = (ch << 6) | (temp & 0x3f);
836
837 if (ch < 0x800)
838 {
839 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
840 return (EOF);
841 }
842
843 /*
844 * Ignore (strip) Byte Order Mark (BOM)...
845 */
846
847 if (ch == 0xfeff)
848 return (mxml_fd_getc(p, encoding));
849 }
850 else if ((ch & 0xf8) == 0xf0)
851 {
852 /*
853 * Four-byte value...
854 */
855
856 if (buf->current >= buf->end)
857 if (mxml_fd_read(buf) < 0)
858 return (EOF);
859
860 temp = *(buf->current)++;
861
862 if ((temp & 0xc0) != 0x80)
863 return (EOF);
864
865 ch = ((ch & 0x07) << 6) | (temp & 0x3f);
866
867 if (buf->current >= buf->end)
868 if (mxml_fd_read(buf) < 0)
869 return (EOF);
870
871 temp = *(buf->current)++;
872
873 if ((temp & 0xc0) != 0x80)
874 return (EOF);
875
876 ch = (ch << 6) | (temp & 0x3f);
877
878 if (buf->current >= buf->end)
879 if (mxml_fd_read(buf) < 0)
880 return (EOF);
881
882 temp = *(buf->current)++;
883
884 if ((temp & 0xc0) != 0x80)
885 return (EOF);
886
887 ch = (ch << 6) | (temp & 0x3f);
888
889 if (ch < 0x10000)
890 {
891 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
892 return (EOF);
893 }
894 }
895 else
896 return (EOF);
897 break;
898
899 case ENCODE_UTF16BE :
900 /*
901 * Read UTF-16 big-endian char...
902 */
903
904 if (buf->current >= buf->end)
905 if (mxml_fd_read(buf) < 0)
906 return (EOF);
907
908 temp = *(buf->current)++;
909
910 ch = (ch << 8) | temp;
911
912 if (mxml_bad_char(ch))
913 {
914 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
915 ch);
916 return (EOF);
917 }
918 else if (ch >= 0xd800 && ch <= 0xdbff)
919 {
920 /*
921 * Multi-word UTF-16 char...
922 */
923
924 int lch;
925
926 if (buf->current >= buf->end)
927 if (mxml_fd_read(buf) < 0)
928 return (EOF);
929
930 lch = *(buf->current)++;
931
932 if (buf->current >= buf->end)
933 if (mxml_fd_read(buf) < 0)
934 return (EOF);
935
936 temp = *(buf->current)++;
937
938 lch = (lch << 8) | temp;
939
940 if (lch < 0xdc00 || lch >= 0xdfff)
941 return (EOF);
942
943 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
944 }
945 break;
946
947 case ENCODE_UTF16LE :
948 /*
949 * Read UTF-16 little-endian char...
950 */
951
952 if (buf->current >= buf->end)
953 if (mxml_fd_read(buf) < 0)
954 return (EOF);
955
956 temp = *(buf->current)++;
957
958 ch |= (temp << 8);
959
960 if (mxml_bad_char(ch))
961 {
962 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
963 ch);
964 return (EOF);
965 }
966 else if (ch >= 0xd800 && ch <= 0xdbff)
967 {
968 /*
969 * Multi-word UTF-16 char...
970 */
971
972 int lch;
973
974 if (buf->current >= buf->end)
975 if (mxml_fd_read(buf) < 0)
976 return (EOF);
977
978 lch = *(buf->current)++;
979
980 if (buf->current >= buf->end)
981 if (mxml_fd_read(buf) < 0)
982 return (EOF);
983
984 temp = *(buf->current)++;
985
986 lch |= (temp << 8);
987
988 if (lch < 0xdc00 || lch >= 0xdfff)
989 return (EOF);
990
991 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
992 }
993 break;
994 }
995
996#if DEBUG > 1
997 printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
998#endif /* DEBUG > 1 */
999
1000 return (ch);
1001}
1002
1003
1004/*
1005 * 'mxml_fd_putc()' - Write a character to a file descriptor.
1006 */
1007
1008static int /* O - 0 on success, -1 on error */
1009mxml_fd_putc(int ch, /* I - Character */
1010 void *p) /* I - File descriptor buffer */
1011{
1012 _mxml_fdbuf_t *buf; /* File descriptor buffer */
1013
1014
1015 /*
1016 * Flush the write buffer as needed...
1017 */
1018
1019 buf = (_mxml_fdbuf_t *)p;
1020
1021 if (buf->current >= buf->end)
1022 if (mxml_fd_write(buf) < 0)
1023 return (-1);
1024
1025 *(buf->current)++ = ch;
1026
1027 /*
1028 * Return successfully...
1029 */
1030
1031 return (0);
1032}
1033
1034
1035/*
1036 * 'mxml_fd_read()' - Read a buffer of data from a file descriptor.
1037 */
1038
1039static int /* O - 0 on success, -1 on error */
1040mxml_fd_read(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */
1041{
1042 int bytes; /* Bytes read... */
1043
1044
1045 /*
1046 * Range check input...
1047 */
1048
1049 if (!buf)
1050 return (-1);
1051
1052 /*
1053 * Read from the file descriptor...
1054 */
1055
1056 while ((bytes = read(buf->fd, buf->buffer, sizeof(buf->buffer))) < 0)
1057#ifdef EINTR
1058 if (errno != EAGAIN && errno != EINTR)
1059#else
1060 if (errno != EAGAIN)
1061#endif /* EINTR */
1062 return (-1);
1063
1064 if (bytes == 0)
1065 return (-1);
1066
1067 /*
1068 * Update the pointers and return success...
1069 */
1070
1071 buf->current = buf->buffer;
1072 buf->end = buf->buffer + bytes;
1073
1074 return (0);
1075}
1076
1077
1078/*
1079 * 'mxml_fd_write()' - Write a buffer of data to a file descriptor.
1080 */
1081
1082static int /* O - 0 on success, -1 on error */
1083mxml_fd_write(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */
1084{
1085 int bytes; /* Bytes written */
1086 unsigned char *ptr; /* Pointer into buffer */
1087
1088
1089 /*
1090 * Range check...
1091 */
1092
1093 if (!buf)
1094 return (-1);
1095
1096 /*
1097 * Return 0 if there is nothing to write...
1098 */
1099
1100 if (buf->current == buf->buffer)
1101 return (0);
1102
1103 /*
1104 * Loop until we have written everything...
1105 */
1106
1107 for (ptr = buf->buffer; ptr < buf->current; ptr += bytes)
1108 if ((bytes = write(buf->fd, ptr, buf->current - ptr)) < 0)
1109 return (-1);
1110
1111 /*
1112 * All done, reset pointers and return success...
1113 */
1114
1115 buf->current = buf->buffer;
1116
1117 return (0);
1118}
1119
1120
1121/*
1122 * 'mxml_file_getc()' - Get a character from a file.
1123 */
1124
1125static int /* O - Character or EOF */
1126mxml_file_getc(void *p, /* I - Pointer to file */
1127 int *encoding) /* IO - Encoding */
1128{
1129 int ch, /* Character from file */
1130 temp; /* Temporary character */
1131 FILE *fp; /* Pointer to file */
1132
1133
1134 /*
1135 * Read a character from the file and see if it is EOF or ASCII...
1136 */
1137
1138 fp = (FILE *)p;
1139 ch = getc(fp);
1140
1141 if (ch == EOF)
1142 return (EOF);
1143
1144 switch (*encoding)
1145 {
1146 case ENCODE_UTF8 :
1147 /*
1148 * Got a UTF-8 character; convert UTF-8 to Unicode and return...
1149 */
1150
1151 if (!(ch & 0x80))
1152 {
1153 if (mxml_bad_char(ch))
1154 {
1155 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
1156 ch);
1157 return (EOF);
1158 }
1159
1160#if DEBUG > 1
1161 printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
1162#endif /* DEBUG > 1 */
1163
1164 return (ch);
1165 }
1166 else if (ch == 0xfe)
1167 {
1168 /*
1169 * UTF-16 big-endian BOM?
1170 */
1171
1172 ch = getc(fp);
1173 if (ch != 0xff)
1174 return (EOF);
1175
1176 *encoding = ENCODE_UTF16BE;
1177
1178 return (mxml_file_getc(p, encoding));
1179 }
1180 else if (ch == 0xff)
1181 {
1182 /*
1183 * UTF-16 little-endian BOM?
1184 */
1185
1186 ch = getc(fp);
1187 if (ch != 0xfe)
1188 return (EOF);
1189
1190 *encoding = ENCODE_UTF16LE;
1191
1192 return (mxml_file_getc(p, encoding));
1193 }
1194 else if ((ch & 0xe0) == 0xc0)
1195 {
1196 /*
1197 * Two-byte value...
1198 */
1199
1200 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1201 return (EOF);
1202
1203 ch = ((ch & 0x1f) << 6) | (temp & 0x3f);
1204
1205 if (ch < 0x80)
1206 {
1207 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
1208 return (EOF);
1209 }
1210 }
1211 else if ((ch & 0xf0) == 0xe0)
1212 {
1213 /*
1214 * Three-byte value...
1215 */
1216
1217 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1218 return (EOF);
1219
1220 ch = ((ch & 0x0f) << 6) | (temp & 0x3f);
1221
1222 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1223 return (EOF);
1224
1225 ch = (ch << 6) | (temp & 0x3f);
1226
1227 if (ch < 0x800)
1228 {
1229 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
1230 return (EOF);
1231 }
1232
1233 /*
1234 * Ignore (strip) Byte Order Mark (BOM)...
1235 */
1236
1237 if (ch == 0xfeff)
1238 return (mxml_file_getc(p, encoding));
1239 }
1240 else if ((ch & 0xf8) == 0xf0)
1241 {
1242 /*
1243 * Four-byte value...
1244 */
1245
1246 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1247 return (EOF);
1248
1249 ch = ((ch & 0x07) << 6) | (temp & 0x3f);
1250
1251 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1252 return (EOF);
1253
1254 ch = (ch << 6) | (temp & 0x3f);
1255
1256 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1257 return (EOF);
1258
1259 ch = (ch << 6) | (temp & 0x3f);
1260
1261 if (ch < 0x10000)
1262 {
1263 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
1264 return (EOF);
1265 }
1266 }
1267 else
1268 return (EOF);
1269 break;
1270
1271 case ENCODE_UTF16BE :
1272 /*
1273 * Read UTF-16 big-endian char...
1274 */
1275
1276 ch = (ch << 8) | getc(fp);
1277
1278 if (mxml_bad_char(ch))
1279 {
1280 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
1281 ch);
1282 return (EOF);
1283 }
1284 else if (ch >= 0xd800 && ch <= 0xdbff)
1285 {
1286 /*
1287 * Multi-word UTF-16 char...
1288 */
1289
1290 int lch = (getc(fp) << 8) | getc(fp);
1291
1292 if (lch < 0xdc00 || lch >= 0xdfff)
1293 return (EOF);
1294
1295 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
1296 }
1297 break;
1298
1299 case ENCODE_UTF16LE :
1300 /*
1301 * Read UTF-16 little-endian char...
1302 */
1303
1304 ch |= (getc(fp) << 8);
1305
1306 if (mxml_bad_char(ch))
1307 {
1308 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
1309 ch);
1310 return (EOF);
1311 }
1312 else if (ch >= 0xd800 && ch <= 0xdbff)
1313 {
1314 /*
1315 * Multi-word UTF-16 char...
1316 */
1317
1318 int lch = getc(fp) | (getc(fp) << 8);
1319
1320 if (lch < 0xdc00 || lch >= 0xdfff)
1321 return (EOF);
1322
1323 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
1324 }
1325 break;
1326 }
1327
1328#if DEBUG > 1
1329 printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
1330#endif /* DEBUG > 1 */
1331
1332 return (ch);
1333}
1334
1335
1336/*
1337 * 'mxml_file_putc()' - Write a character to a file.
1338 */
1339
1340static int /* O - 0 on success, -1 on failure */
1341mxml_file_putc(int ch, /* I - Character to write */
1342 void *p) /* I - Pointer to file */
1343{
1344 return (putc(ch, (FILE *)p) == EOF ? -1 : 0);
1345}
1346
1347
1348/*
1349 * 'mxml_get_entity()' - Get the character corresponding to an entity...
1350 */
1351
1352static int /* O - Character value or EOF on error */
1353mxml_get_entity(mxml_node_t *parent, /* I - Parent node */
1354 void *p, /* I - Pointer to source */
1355 int *encoding, /* IO - Character encoding */
1356 int (*getc_cb)(void *, int *))
1357 /* I - Get character function */
1358{
1359 int ch; /* Current character */
1360 char entity[64], /* Entity string */
1361 *entptr; /* Pointer into entity */
1362
1363
1364 entptr = entity;
1365
1366 while ((ch = (*getc_cb)(p, encoding)) != EOF)
1367 if (ch > 126 || (!isalnum(ch) && ch != '#'))
1368 break;
1369 else if (entptr < (entity + sizeof(entity) - 1))
1370 *entptr++ = ch;
1371 else
1372 {
1373 mxml_error("Entity name too long under parent <%s>!",
1374 parent ? parent->value.element.name : "null");
1375 break;
1376 }
1377
1378 *entptr = '\0';
1379
1380 if (ch != ';')
1381 {
1382 mxml_error("Character entity \"%s\" not terminated under parent <%s>!",
1383 entity, parent ? parent->value.element.name : "null");
1384 return (EOF);
1385 }
1386
1387 if (entity[0] == '#')
1388 {
1389 if (entity[1] == 'x')
1390 ch = strtol(entity + 2, NULL, 16);
1391 else
1392 ch = strtol(entity + 1, NULL, 10);
1393 }
1394 else if ((ch = mxmlEntityGetValue(entity)) < 0)
1395 mxml_error("Entity name \"%s;\" not supported under parent <%s>!",
1396 entity, parent ? parent->value.element.name : "null");
1397
1398 if (mxml_bad_char(ch))
1399 {
1400 mxml_error("Bad control character 0x%02x under parent <%s> not allowed by XML standard!",
1401 ch, parent ? parent->value.element.name : "null");
1402 return (EOF);
1403 }
1404
1405 return (ch);
1406}
1407
1408
1409/*
1410 * 'mxml_load_data()' - Load data into an XML node tree.
1411 */
1412
1413static mxml_node_t * /* O - First node or NULL if the file could not be read. */
1414mxml_load_data(
1415 mxml_node_t *top, /* I - Top node */
1416 void *p, /* I - Pointer to data */
1417 mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
1418 _mxml_getc_cb_t getc_cb, /* I - Read function */
1419 mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
1420 void *sax_data) /* I - SAX user data */
1421{
1422 mxml_node_t *node, /* Current node */
1423 *first, /* First node added */
1424 *parent; /* Current parent node */
1425 int ch, /* Character from file */
1426 whitespace; /* Non-zero if whitespace seen */
1427 char *buffer, /* String buffer */
1428 *bufptr; /* Pointer into buffer */
1429 int bufsize; /* Size of buffer */
1430 mxml_type_t type; /* Current node type */
1431 int encoding; /* Character encoding */
1432 _mxml_global_t *global = _mxml_global();
1433 /* Global data */
1434 static const char * const types[] = /* Type strings... */
1435 {
1436 "MXML_ELEMENT", /* XML element with attributes */
1437 "MXML_INTEGER", /* Integer value */
1438 "MXML_OPAQUE", /* Opaque string */
1439 "MXML_REAL", /* Real value */
1440 "MXML_TEXT", /* Text fragment */
1441 "MXML_CUSTOM" /* Custom data */
1442 };
1443
1444
1445 /*
1446 * Read elements and other nodes from the file...
1447 */
1448
1449 if ((buffer = malloc(64)) == NULL)
1450 {
1451 mxml_error("Unable to allocate string buffer!");
1452 return (NULL);
1453 }
1454
1455 bufsize = 64;
1456 bufptr = buffer;
1457 parent = top;
1458 first = NULL;
1459 whitespace = 0;
1460 encoding = ENCODE_UTF8;
1461
1462 if (cb && parent)
1463 type = (*cb)(parent);
1464 else
1465 type = MXML_TEXT;
1466
1467 while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1468 {
1469 if ((ch == '<' ||
1470 (mxml_isspace(ch) && type != MXML_OPAQUE && type != MXML_CUSTOM)) &&
1471 bufptr > buffer)
1472 {
1473 /*
1474 * Add a new value node...
1475 */
1476
1477 *bufptr = '\0';
1478
1479 switch (type)
1480 {
1481 case MXML_INTEGER :
1482 node = mxmlNewInteger(parent, strtol(buffer, &bufptr, 0));
1483 break;
1484
1485 case MXML_OPAQUE :
1486 node = mxmlNewOpaque(parent, buffer);
1487 break;
1488
1489 case MXML_REAL :
1490 node = mxmlNewReal(parent, strtod(buffer, &bufptr));
1491 break;
1492
1493 case MXML_TEXT :
1494 node = mxmlNewText(parent, whitespace, buffer);
1495 break;
1496
1497 case MXML_CUSTOM :
1498 if (global->custom_load_cb)
1499 {
1500 /*
1501 * Use the callback to fill in the custom data...
1502 */
1503
1504 node = mxmlNewCustom(parent, NULL, NULL);
1505
1506 if ((*global->custom_load_cb)(node, buffer))
1507 {
1508 mxml_error("Bad custom value '%s' in parent <%s>!",
1509 buffer, parent ? parent->value.element.name : "null");
1510 mxmlDelete(node);
1511 node = NULL;
1512 }
1513 break;
1514 }
1515
1516 default : /* Ignore... */
1517 node = NULL;
1518 break;
1519 }
1520
1521 if (*bufptr)
1522 {
1523 /*
1524 * Bad integer/real number value...
1525 */
1526
1527 mxml_error("Bad %s value '%s' in parent <%s>!",
1528 type == MXML_INTEGER ? "integer" : "real", buffer,
1529 parent ? parent->value.element.name : "null");
1530 break;
1531 }
1532
1533 bufptr = buffer;
1534 whitespace = mxml_isspace(ch) && type == MXML_TEXT;
1535
1536 if (!node && type != MXML_IGNORE)
1537 {
1538 /*
1539 * Print error and return...
1540 */
1541
1542 mxml_error("Unable to add value node of type %s to parent <%s>!",
1543 types[type], parent ? parent->value.element.name : "null");
1544 goto error;
1545 }
1546
1547 if (sax_cb)
1548 {
1549 (*sax_cb)(node, MXML_SAX_DATA, sax_data);
1550
1551 if (!mxmlRelease(node))
1552 node = NULL;
1553 }
1554
1555 if (!first && node)
1556 first = node;
1557 }
1558 else if (mxml_isspace(ch) && type == MXML_TEXT)
1559 whitespace = 1;
1560
1561 /*
1562 * Add lone whitespace node if we have an element and existing
1563 * whitespace...
1564 */
1565
1566 if (ch == '<' && whitespace && type == MXML_TEXT)
1567 {
1568 if (parent)
1569 {
1570 node = mxmlNewText(parent, whitespace, "");
1571
1572 if (sax_cb)
1573 {
1574 (*sax_cb)(node, MXML_SAX_DATA, sax_data);
1575
1576 if (!mxmlRelease(node))
1577 node = NULL;
1578 }
1579
1580 if (!first && node)
1581 first = node;
1582 }
1583
1584 whitespace = 0;
1585 }
1586
1587 if (ch == '<')
1588 {
1589 /*
1590 * Start of open/close tag...
1591 */
1592
1593 bufptr = buffer;
1594
1595 while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1596 if (mxml_isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer))
1597 break;
1598 else if (ch == '<')
1599 {
1600 mxml_error("Bare < in element!");
1601 goto error;
1602 }
1603 else if (ch == '&')
1604 {
1605 if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
1606 goto error;
1607
1608 if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1609 goto error;
1610 }
1611 else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1612 goto error;
1613 else if (((bufptr - buffer) == 1 && buffer[0] == '?') ||
1614 ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) ||
1615 ((bufptr - buffer) == 8 && !strncmp(buffer, "![CDATA[", 8)))
1616 break;
1617
1618 *bufptr = '\0';
1619
1620 if (!strcmp(buffer, "!--"))
1621 {
1622 /*
1623 * Gather rest of comment...
1624 */
1625
1626 while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1627 {
1628 if (ch == '>' && bufptr > (buffer + 4) &&
1629 bufptr[-3] != '-' && bufptr[-2] == '-' && bufptr[-1] == '-')
1630 break;
1631 else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1632 goto error;
1633 }
1634
1635 /*
1636 * Error out if we didn't get the whole comment...
1637 */
1638
1639 if (ch != '>')
1640 {
1641 /*
1642 * Print error and return...
1643 */
1644
1645 mxml_error("Early EOF in comment node!");
1646 goto error;
1647 }
1648
1649
1650 /*
1651 * Otherwise add this as an element under the current parent...
1652 */
1653
1654 *bufptr = '\0';
1655
1656 if (!parent && first)
1657 {
1658 /*
1659 * There can only be one root element!
1660 */
1661
1662 mxml_error("<%s> cannot be a second root node after <%s>",
1663 buffer, first->value.element.name);
1664 goto error;
1665 }
1666
1667 if ((node = mxmlNewElement(parent, buffer)) == NULL)
1668 {
1669 /*
1670 * Just print error for now...
1671 */
1672
1673 mxml_error("Unable to add comment node to parent <%s>!",
1674 parent ? parent->value.element.name : "null");
1675 break;
1676 }
1677
1678 if (sax_cb)
1679 {
1680 (*sax_cb)(node, MXML_SAX_COMMENT, sax_data);
1681
1682 if (!mxmlRelease(node))
1683 node = NULL;
1684 }
1685
1686 if (node && !first)
1687 first = node;
1688 }
1689 else if (!strcmp(buffer, "![CDATA["))
1690 {
1691 /*
1692 * Gather CDATA section...
1693 */
1694
1695 while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1696 {
1697 if (ch == '>' && !strncmp(bufptr - 2, "]]", 2))
1698 break;
1699 else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1700 goto error;
1701 }
1702
1703 /*
1704 * Error out if we didn't get the whole comment...
1705 */
1706
1707 if (ch != '>')
1708 {
1709 /*
1710 * Print error and return...
1711 */
1712
1713 mxml_error("Early EOF in CDATA node!");
1714 goto error;
1715 }
1716
1717
1718 /*
1719 * Otherwise add this as an element under the current parent...
1720 */
1721
1722 *bufptr = '\0';
1723
1724 if (!parent && first)
1725 {
1726 /*
1727 * There can only be one root element!
1728 */
1729
1730 mxml_error("<%s> cannot be a second root node after <%s>",
1731 buffer, first->value.element.name);
1732 goto error;
1733 }
1734
1735 if ((node = mxmlNewElement(parent, buffer)) == NULL)
1736 {
1737 /*
1738 * Print error and return...
1739 */
1740
1741 mxml_error("Unable to add CDATA node to parent <%s>!",
1742 parent ? parent->value.element.name : "null");
1743 goto error;
1744 }
1745
1746 if (sax_cb)
1747 {
1748 (*sax_cb)(node, MXML_SAX_CDATA, sax_data);
1749
1750 if (!mxmlRelease(node))
1751 node = NULL;
1752 }
1753
1754 if (node && !first)
1755 first = node;
1756 }
1757 else if (buffer[0] == '?')
1758 {
1759 /*
1760 * Gather rest of processing instruction...
1761 */
1762
1763 while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1764 {
1765 if (ch == '>' && bufptr > buffer && bufptr[-1] == '?')
1766 break;
1767 else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1768 goto error;
1769 }
1770
1771 /*
1772 * Error out if we didn't get the whole processing instruction...
1773 */
1774
1775 if (ch != '>')
1776 {
1777 /*
1778 * Print error and return...
1779 */
1780
1781 mxml_error("Early EOF in processing instruction node!");
1782 goto error;
1783 }
1784
1785 /*
1786 * Otherwise add this as an element under the current parent...
1787 */
1788
1789 *bufptr = '\0';
1790
1791 if (!parent && first)
1792 {
1793 /*
1794 * There can only be one root element!
1795 */
1796
1797 mxml_error("<%s> cannot be a second root node after <%s>",
1798 buffer, first->value.element.name);
1799 goto error;
1800 }
1801
1802 if ((node = mxmlNewElement(parent, buffer)) == NULL)
1803 {
1804 /*
1805 * Print error and return...
1806 */
1807
1808 mxml_error("Unable to add processing instruction node to parent <%s>!",
1809 parent ? parent->value.element.name : "null");
1810 goto error;
1811 }
1812
1813 if (sax_cb)
1814 {
1815 (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data);
1816
1817 if (!mxmlRelease(node))
1818 node = NULL;
1819 }
1820
1821 if (node)
1822 {
1823 if (!first)
1824 first = node;
1825
1826 if (!parent)
1827 {
1828 parent = node;
1829
1830 if (cb)
1831 type = (*cb)(parent);
1832 }
1833 }
1834 }
1835 else if (buffer[0] == '!')
1836 {
1837 /*
1838 * Gather rest of declaration...
1839 */
1840
1841 do
1842 {
1843 if (ch == '>')
1844 break;
1845 else
1846 {
1847 if (ch == '&')
1848 if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
1849 goto error;
1850
1851 if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1852 goto error;
1853 }
1854 }
1855 while ((ch = (*getc_cb)(p, &encoding)) != EOF);
1856
1857 /*
1858 * Error out if we didn't get the whole declaration...
1859 */
1860
1861 if (ch != '>')
1862 {
1863 /*
1864 * Print error and return...
1865 */
1866
1867 mxml_error("Early EOF in declaration node!");
1868 goto error;
1869 }
1870
1871 /*
1872 * Otherwise add this as an element under the current parent...
1873 */
1874
1875 *bufptr = '\0';
1876
1877 if (!parent && first)
1878 {
1879 /*
1880 * There can only be one root element!
1881 */
1882
1883 mxml_error("<%s> cannot be a second root node after <%s>",
1884 buffer, first->value.element.name);
1885 goto error;
1886 }
1887
1888 if ((node = mxmlNewElement(parent, buffer)) == NULL)
1889 {
1890 /*
1891 * Print error and return...
1892 */
1893
1894 mxml_error("Unable to add declaration node to parent <%s>!",
1895 parent ? parent->value.element.name : "null");
1896 goto error;
1897 }
1898
1899 if (sax_cb)
1900 {
1901 (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data);
1902
1903 if (!mxmlRelease(node))
1904 node = NULL;
1905 }
1906
1907 if (node)
1908 {
1909 if (!first)
1910 first = node;
1911
1912 if (!parent)
1913 {
1914 parent = node;
1915
1916 if (cb)
1917 type = (*cb)(parent);
1918 }
1919 }
1920 }
1921 else if (buffer[0] == '/')
1922 {
1923 /*
1924 * Handle close tag...
1925 */
1926
1927 if (!parent || strcmp(buffer + 1, parent->value.element.name))
1928 {
1929 /*
1930 * Close tag doesn't match tree; print an error for now...
1931 */
1932
1933 mxml_error("Mismatched close tag <%s> under parent <%s>!",
1934 buffer, parent ? parent->value.element.name : "(null)");
1935 goto error;
1936 }
1937
1938 /*
1939 * Keep reading until we see >...
1940 */
1941
1942 while (ch != '>' && ch != EOF)
1943 ch = (*getc_cb)(p, &encoding);
1944
1945 node = parent;
1946 parent = parent->parent;
1947
1948 if (sax_cb)
1949 {
1950 (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data);
1951
1952 if (!mxmlRelease(node) && first == node)
1953 first = NULL;
1954 }
1955
1956 /*
1957 * Ascend into the parent and set the value type as needed...
1958 */
1959
1960 if (cb && parent)
1961 type = (*cb)(parent);
1962 }
1963 else
1964 {
1965 /*
1966 * Handle open tag...
1967 */
1968
1969 if (!parent && first)
1970 {
1971 /*
1972 * There can only be one root element!
1973 */
1974
1975 mxml_error("<%s> cannot be a second root node after <%s>",
1976 buffer, first->value.element.name);
1977 goto error;
1978 }
1979
1980 if ((node = mxmlNewElement(parent, buffer)) == NULL)
1981 {
1982 /*
1983 * Just print error for now...
1984 */
1985
1986 mxml_error("Unable to add element node to parent <%s>!",
1987 parent ? parent->value.element.name : "null");
1988 goto error;
1989 }
1990
1991 if (mxml_isspace(ch))
1992 {
1993 if ((ch = mxml_parse_element(node, p, &encoding, getc_cb)) == EOF)
1994 goto error;
1995 }
1996 else if (ch == '/')
1997 {
1998 if ((ch = (*getc_cb)(p, &encoding)) != '>')
1999 {
2000 mxml_error("Expected > but got '%c' instead for element <%s/>!",
2001 ch, buffer);
2002 mxmlDelete(node);
2003 goto error;
2004 }
2005
2006 ch = '/';
2007 }
2008
2009 if (sax_cb)
2010 (*sax_cb)(node, MXML_SAX_ELEMENT_OPEN, sax_data);
2011
2012 if (!first)
2013 first = node;
2014
2015 if (ch == EOF)
2016 break;
2017
2018 if (ch != '/')
2019 {
2020 /*
2021 * Descend into this node, setting the value type as needed...
2022 */
2023
2024 parent = node;
2025
2026 if (cb && parent)
2027 type = (*cb)(parent);
2028 }
2029 else if (sax_cb)
2030 {
2031 (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data);
2032
2033 if (!mxmlRelease(node) && first == node)
2034 first = NULL;
2035 }
2036 }
2037
2038 bufptr = buffer;
2039 }
2040 else if (ch == '&')
2041 {
2042 /*
2043 * Add character entity to current buffer...
2044 */
2045
2046 if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
2047 goto error;
2048
2049 if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
2050 goto error;
2051 }
2052 else if (type == MXML_OPAQUE || type == MXML_CUSTOM || !mxml_isspace(ch))
2053 {
2054 /*
2055 * Add character to current buffer...
2056 */
2057
2058 if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
2059 goto error;
2060 }
2061 }
2062
2063 /*
2064 * Free the string buffer - we don't need it anymore...
2065 */
2066
2067 free(buffer);
2068
2069 /*
2070 * Find the top element and return it...
2071 */
2072
2073 if (parent)
2074 {
2075 node = parent;
2076
2077 while (parent->parent != top && parent->parent)
2078 parent = parent->parent;
2079
2080 if (node != parent)
2081 {
2082 mxml_error("Missing close tag </%s> under parent <%s>!",
2083 node->value.element.name,
2084 node->parent ? node->parent->value.element.name : "(null)");
2085
2086 mxmlDelete(first);
2087
2088 return (NULL);
2089 }
2090 }
2091
2092 if (parent)
2093 return (parent);
2094 else
2095 return (first);
2096
2097 /*
2098 * Common error return...
2099 */
2100
2101error:
2102
2103 mxmlDelete(first);
2104
2105 free(buffer);
2106
2107 return (NULL);
2108}
2109
2110
2111/*
2112 * 'mxml_parse_element()' - Parse an element for any attributes...
2113 */
2114
2115static int /* O - Terminating character */
2116mxml_parse_element(
2117 mxml_node_t *node, /* I - Element node */
2118 void *p, /* I - Data to read from */
2119 int *encoding, /* IO - Encoding */
2120 _mxml_getc_cb_t getc_cb) /* I - Data callback */
2121{
2122 int ch, /* Current character in file */
2123 quote; /* Quoting character */
2124 char *name, /* Attribute name */
2125 *value, /* Attribute value */
2126 *ptr; /* Pointer into name/value */
2127 int namesize, /* Size of name string */
2128 valsize; /* Size of value string */
2129
2130
2131 /*
2132 * Initialize the name and value buffers...
2133 */
2134
2135 if ((name = malloc(64)) == NULL)
2136 {
2137 mxml_error("Unable to allocate memory for name!");
2138 return (EOF);
2139 }
2140
2141 namesize = 64;
2142
2143 if ((value = malloc(64)) == NULL)
2144 {
2145 free(name);
2146 mxml_error("Unable to allocate memory for value!");
2147 return (EOF);
2148 }
2149
2150 valsize = 64;
2151
2152 /*
2153 * Loop until we hit a >, /, ?, or EOF...
2154 */
2155
2156 while ((ch = (*getc_cb)(p, encoding)) != EOF)
2157 {
2158#if DEBUG > 1
2159 fprintf(stderr, "parse_element: ch='%c'\n", ch);
2160#endif /* DEBUG > 1 */
2161
2162 /*
2163 * Skip leading whitespace...
2164 */
2165
2166 if (mxml_isspace(ch))
2167 continue;
2168
2169 /*
2170 * Stop at /, ?, or >...
2171 */
2172
2173 if (ch == '/' || ch == '?')
2174 {
2175 /*
2176 * Grab the > character and print an error if it isn't there...
2177 */
2178
2179 quote = (*getc_cb)(p, encoding);
2180
2181 if (quote != '>')
2182 {
2183 mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
2184 ch, node->value.element.name, quote);
2185 goto error;
2186 }
2187
2188 break;
2189 }
2190 else if (ch == '<')
2191 {
2192 mxml_error("Bare < in element %s!", node->value.element.name);
2193 goto error;
2194 }
2195 else if (ch == '>')
2196 break;
2197
2198 /*
2199 * Read the attribute name...
2200 */
2201
2202 name[0] = ch;
2203 ptr = name + 1;
2204
2205 if (ch == '\"' || ch == '\'')
2206 {
2207 /*
2208 * Name is in quotes, so get a quoted string...
2209 */
2210
2211 quote = ch;
2212
2213 while ((ch = (*getc_cb)(p, encoding)) != EOF)
2214 {
2215 if (ch == '&')
2216 if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2217 goto error;
2218
2219 if (mxml_add_char(ch, &ptr, &name, &namesize))
2220 goto error;
2221
2222 if (ch == quote)
2223 break;
2224 }
2225 }
2226 else
2227 {
2228 /*
2229 * Grab an normal, non-quoted name...
2230 */
2231
2232 while ((ch = (*getc_cb)(p, encoding)) != EOF)
2233 if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>' ||
2234 ch == '?')
2235 break;
2236 else
2237 {
2238 if (ch == '&')
2239 if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2240 goto error;
2241
2242 if (mxml_add_char(ch, &ptr, &name, &namesize))
2243 goto error;
2244 }
2245 }
2246
2247 *ptr = '\0';
2248
2249 if (mxmlElementGetAttr(node, name))
2250 goto error;
2251
2252 while (ch != EOF && mxml_isspace(ch))
2253 ch = (*getc_cb)(p, encoding);
2254
2255 if (ch == '=')
2256 {
2257 /*
2258 * Read the attribute value...
2259 */
2260
2261 while ((ch = (*getc_cb)(p, encoding)) != EOF && mxml_isspace(ch));
2262
2263 if (ch == EOF)
2264 {
2265 mxml_error("Missing value for attribute '%s' in element %s!",
2266 name, node->value.element.name);
2267 goto error;
2268 }
2269
2270 if (ch == '\'' || ch == '\"')
2271 {
2272 /*
2273 * Read quoted value...
2274 */
2275
2276 quote = ch;
2277 ptr = value;
2278
2279 while ((ch = (*getc_cb)(p, encoding)) != EOF)
2280 if (ch == quote)
2281 break;
2282 else
2283 {
2284 if (ch == '&')
2285 if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2286 goto error;
2287
2288 if (mxml_add_char(ch, &ptr, &value, &valsize))
2289 goto error;
2290 }
2291
2292 *ptr = '\0';
2293 }
2294 else
2295 {
2296 /*
2297 * Read unquoted value...
2298 */
2299
2300 value[0] = ch;
2301 ptr = value + 1;
2302
2303 while ((ch = (*getc_cb)(p, encoding)) != EOF)
2304 if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>')
2305 break;
2306 else
2307 {
2308 if (ch == '&')
2309 if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2310 goto error;
2311
2312 if (mxml_add_char(ch, &ptr, &value, &valsize))
2313 goto error;
2314 }
2315
2316 *ptr = '\0';
2317 }
2318
2319 /*
2320 * Set the attribute with the given string value...
2321 */
2322
2323 mxmlElementSetAttr(node, name, value);
2324 }
2325 else
2326 {
2327 mxml_error("Missing value for attribute '%s' in element %s!",
2328 name, node->value.element.name);
2329 goto error;
2330 }
2331
2332 /*
2333 * Check the end character...
2334 */
2335
2336 if (ch == '/' || ch == '?')
2337 {
2338 /*
2339 * Grab the > character and print an error if it isn't there...
2340 */
2341
2342 quote = (*getc_cb)(p, encoding);
2343
2344 if (quote != '>')
2345 {
2346 mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
2347 ch, node->value.element.name, quote);
2348 ch = EOF;
2349 }
2350
2351 break;
2352 }
2353 else if (ch == '>')
2354 break;
2355 }
2356
2357 /*
2358 * Free the name and value buffers and return...
2359 */
2360
2361 free(name);
2362 free(value);
2363
2364 return (ch);
2365
2366 /*
2367 * Common error return point...
2368 */
2369
2370error:
2371
2372 free(name);
2373 free(value);
2374
2375 return (EOF);
2376}
2377
2378
2379/*
2380 * 'mxml_string_getc()' - Get a character from a string.
2381 */
2382
2383static int /* O - Character or EOF */
2384mxml_string_getc(void *p, /* I - Pointer to file */
2385 int *encoding) /* IO - Encoding */
2386{
2387 int ch; /* Character */
2388 const char **s; /* Pointer to string pointer */
2389
2390
2391 s = (const char **)p;
2392
2393 if ((ch = (*s)[0] & 255) != 0 || *encoding == ENCODE_UTF16LE)
2394 {
2395 /*
2396 * Got character; convert UTF-8 to integer and return...
2397 */
2398
2399 (*s)++;
2400
2401 switch (*encoding)
2402 {
2403 case ENCODE_UTF8 :
2404 if (!(ch & 0x80))
2405 {
2406#if DEBUG > 1
2407 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2408#endif /* DEBUG > 1 */
2409
2410 if (mxml_bad_char(ch))
2411 {
2412 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2413 ch);
2414 return (EOF);
2415 }
2416
2417 return (ch);
2418 }
2419 else if (ch == 0xfe)
2420 {
2421 /*
2422 * UTF-16 big-endian BOM?
2423 */
2424
2425 if (((*s)[0] & 255) != 0xff)
2426 return (EOF);
2427
2428 *encoding = ENCODE_UTF16BE;
2429 (*s)++;
2430
2431 return (mxml_string_getc(p, encoding));
2432 }
2433 else if (ch == 0xff)
2434 {
2435 /*
2436 * UTF-16 little-endian BOM?
2437 */
2438
2439 if (((*s)[0] & 255) != 0xfe)
2440 return (EOF);
2441
2442 *encoding = ENCODE_UTF16LE;
2443 (*s)++;
2444
2445 return (mxml_string_getc(p, encoding));
2446 }
2447 else if ((ch & 0xe0) == 0xc0)
2448 {
2449 /*
2450 * Two-byte value...
2451 */
2452
2453 if (((*s)[0] & 0xc0) != 0x80)
2454 return (EOF);
2455
2456 ch = ((ch & 0x1f) << 6) | ((*s)[0] & 0x3f);
2457
2458 (*s)++;
2459
2460 if (ch < 0x80)
2461 {
2462 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
2463 return (EOF);
2464 }
2465
2466#if DEBUG > 1
2467 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2468#endif /* DEBUG > 1 */
2469
2470 return (ch);
2471 }
2472 else if ((ch & 0xf0) == 0xe0)
2473 {
2474 /*
2475 * Three-byte value...
2476 */
2477
2478 if (((*s)[0] & 0xc0) != 0x80 ||
2479 ((*s)[1] & 0xc0) != 0x80)
2480 return (EOF);
2481
2482 ch = ((((ch & 0x0f) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f);
2483
2484 (*s) += 2;
2485
2486 if (ch < 0x800)
2487 {
2488 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
2489 return (EOF);
2490 }
2491
2492 /*
2493 * Ignore (strip) Byte Order Mark (BOM)...
2494 */
2495
2496 if (ch == 0xfeff)
2497 return (mxml_string_getc(p, encoding));
2498
2499#if DEBUG > 1
2500 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2501#endif /* DEBUG > 1 */
2502
2503 return (ch);
2504 }
2505 else if ((ch & 0xf8) == 0xf0)
2506 {
2507 /*
2508 * Four-byte value...
2509 */
2510
2511 if (((*s)[0] & 0xc0) != 0x80 ||
2512 ((*s)[1] & 0xc0) != 0x80 ||
2513 ((*s)[2] & 0xc0) != 0x80)
2514 return (EOF);
2515
2516 ch = ((((((ch & 0x07) << 6) | ((*s)[0] & 0x3f)) << 6) |
2517 ((*s)[1] & 0x3f)) << 6) | ((*s)[2] & 0x3f);
2518
2519 (*s) += 3;
2520
2521 if (ch < 0x10000)
2522 {
2523 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
2524 return (EOF);
2525 }
2526
2527#if DEBUG > 1
2528 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2529#endif /* DEBUG > 1 */
2530
2531 return (ch);
2532 }
2533 else
2534 return (EOF);
2535
2536 case ENCODE_UTF16BE :
2537 /*
2538 * Read UTF-16 big-endian char...
2539 */
2540
2541 ch = (ch << 8) | ((*s)[0] & 255);
2542 (*s) ++;
2543
2544 if (mxml_bad_char(ch))
2545 {
2546 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2547 ch);
2548 return (EOF);
2549 }
2550 else if (ch >= 0xd800 && ch <= 0xdbff)
2551 {
2552 /*
2553 * Multi-word UTF-16 char...
2554 */
2555
2556 int lch; /* Lower word */
2557
2558
2559 if (!(*s)[0])
2560 return (EOF);
2561
2562 lch = (((*s)[0] & 255) << 8) | ((*s)[1] & 255);
2563 (*s) += 2;
2564
2565 if (lch < 0xdc00 || lch >= 0xdfff)
2566 return (EOF);
2567
2568 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
2569 }
2570
2571#if DEBUG > 1
2572 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2573#endif /* DEBUG > 1 */
2574
2575 return (ch);
2576
2577 case ENCODE_UTF16LE :
2578 /*
2579 * Read UTF-16 little-endian char...
2580 */
2581
2582 ch = ch | (((*s)[0] & 255) << 8);
2583
2584 if (!ch)
2585 {
2586 (*s) --;
2587 return (EOF);
2588 }
2589
2590 (*s) ++;
2591
2592 if (mxml_bad_char(ch))
2593 {
2594 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2595 ch);
2596 return (EOF);
2597 }
2598 else if (ch >= 0xd800 && ch <= 0xdbff)
2599 {
2600 /*
2601 * Multi-word UTF-16 char...
2602 */
2603
2604 int lch; /* Lower word */
2605
2606
2607 if (!(*s)[1])
2608 return (EOF);
2609
2610 lch = (((*s)[1] & 255) << 8) | ((*s)[0] & 255);
2611 (*s) += 2;
2612
2613 if (lch < 0xdc00 || lch >= 0xdfff)
2614 return (EOF);
2615
2616 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
2617 }
2618
2619#if DEBUG > 1
2620 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2621#endif /* DEBUG > 1 */
2622
2623 return (ch);
2624 }
2625 }
2626
2627 return (EOF);
2628}
2629
2630
2631/*
2632 * 'mxml_string_putc()' - Write a character to a string.
2633 */
2634
2635static int /* O - 0 on success, -1 on failure */
2636mxml_string_putc(int ch, /* I - Character to write */
2637 void *p) /* I - Pointer to string pointers */
2638{
2639 char **pp; /* Pointer to string pointers */
2640
2641
2642 pp = (char **)p;
2643
2644 if (pp[0] < pp[1])
2645 pp[0][0] = ch;
2646
2647 pp[0] ++;
2648
2649 return (0);
2650}
2651
2652
2653/*
2654 * 'mxml_write_name()' - Write a name string.
2655 */
2656
2657static int /* O - 0 on success, -1 on failure */
2658mxml_write_name(const char *s, /* I - Name to write */
2659 void *p, /* I - Write pointer */
2660 int (*putc_cb)(int, void *))
2661 /* I - Write callback */
2662{
2663 char quote; /* Quote character */
2664 const char *name; /* Entity name */
2665
2666
2667 if (*s == '\"' || *s == '\'')
2668 {
2669 /*
2670 * Write a quoted name string...
2671 */
2672
2673 if ((*putc_cb)(*s, p) < 0)
2674 return (-1);
2675
2676 quote = *s++;
2677
2678 while (*s && *s != quote)
2679 {
2680 if ((name = mxmlEntityGetName(*s)) != NULL)
2681 {
2682 if ((*putc_cb)('&', p) < 0)
2683 return (-1);
2684
2685 while (*name)
2686 {
2687 if ((*putc_cb)(*name, p) < 0)
2688 return (-1);
2689
2690 name ++;
2691 }
2692
2693 if ((*putc_cb)(';', p) < 0)
2694 return (-1);
2695 }
2696 else if ((*putc_cb)(*s, p) < 0)
2697 return (-1);
2698
2699 s ++;
2700 }
2701
2702 /*
2703 * Write the end quote...
2704 */
2705
2706 if ((*putc_cb)(quote, p) < 0)
2707 return (-1);
2708 }
2709 else
2710 {
2711 /*
2712 * Write a non-quoted name string...
2713 */
2714
2715 while (*s)
2716 {
2717 if ((*putc_cb)(*s, p) < 0)
2718 return (-1);
2719
2720 s ++;
2721 }
2722 }
2723
2724 return (0);
2725}
2726
2727
2728/*
2729 * 'mxml_write_node()' - Save an XML node to a file.
2730 */
2731
2732static int /* O - Column or -1 on error */
2733mxml_write_node(mxml_node_t *node, /* I - Node to write */
2734 void *p, /* I - File to write to */
2735 mxml_save_cb_t cb, /* I - Whitespace callback */
2736 int col, /* I - Current column */
2737 _mxml_putc_cb_t putc_cb,/* I - Output callback */
2738 _mxml_global_t *global)/* I - Global data */
2739{
2740 int i, /* Looping var */
2741 width; /* Width of attr + value */
2742 mxml_attr_t *attr; /* Current attribute */
2743 char s[255]; /* Temporary string */
2744
2745
2746 /*
2747 * Print the node value...
2748 */
2749
2750 switch (node->type)
2751 {
2752 case MXML_ELEMENT :
2753 col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
2754
2755 if ((*putc_cb)('<', p) < 0)
2756 return (-1);
2757 if (node->value.element.name[0] == '?' ||
2758 !strncmp(node->value.element.name, "!--", 3) ||
2759 !strncmp(node->value.element.name, "![CDATA[", 8))
2760 {
2761 /*
2762 * Comments, CDATA, and processing instructions do not
2763 * use character entities.
2764 */
2765
2766 const char *ptr; /* Pointer into name */
2767
2768
2769 for (ptr = node->value.element.name; *ptr; ptr ++)
2770 if ((*putc_cb)(*ptr, p) < 0)
2771 return (-1);
2772 }
2773 else if (mxml_write_name(node->value.element.name, p, putc_cb) < 0)
2774 return (-1);
2775
2776 col += strlen(node->value.element.name) + 1;
2777
2778 for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
2779 i > 0;
2780 i --, attr ++)
2781 {
2782 width = strlen(attr->name);
2783
2784 if (attr->value)
2785 width += strlen(attr->value) + 3;
2786
2787 if (global->wrap > 0 && (col + width) > global->wrap)
2788 {
2789 if ((*putc_cb)('\n', p) < 0)
2790 return (-1);
2791
2792 col = 0;
2793 }
2794 else
2795 {
2796 if ((*putc_cb)(' ', p) < 0)
2797 return (-1);
2798
2799 col ++;
2800 }
2801
2802 if (mxml_write_name(attr->name, p, putc_cb) < 0)
2803 return (-1);
2804
2805 if (attr->value)
2806 {
2807 if ((*putc_cb)('=', p) < 0)
2808 return (-1);
2809 if ((*putc_cb)('\"', p) < 0)
2810 return (-1);
2811 if (mxml_write_string(attr->value, p, putc_cb) < 0)
2812 return (-1);
2813 if ((*putc_cb)('\"', p) < 0)
2814 return (-1);
2815 }
2816
2817 col += width;
2818 }
2819
2820 if (node->child)
2821 {
2822 /*
2823 * Write children...
2824 */
2825
2826 mxml_node_t *child; /* Current child */
2827
2828
2829 if ((*putc_cb)('>', p) < 0)
2830 return (-1);
2831 else
2832 col ++;
2833
2834 col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2835
2836 for (child = node->child; child; child = child->next)
2837 {
2838 if ((col = mxml_write_node(child, p, cb, col, putc_cb, global)) < 0)
2839 return (-1);
2840 }
2841
2842 /*
2843 * The ? and ! elements are special-cases and have no end tags...
2844 */
2845
2846 if (node->value.element.name[0] != '!' &&
2847 node->value.element.name[0] != '?')
2848 {
2849 col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb);
2850
2851 if ((*putc_cb)('<', p) < 0)
2852 return (-1);
2853 if ((*putc_cb)('/', p) < 0)
2854 return (-1);
2855 if (mxml_write_string(node->value.element.name, p, putc_cb) < 0)
2856 return (-1);
2857 if ((*putc_cb)('>', p) < 0)
2858 return (-1);
2859
2860 col += strlen(node->value.element.name) + 3;
2861
2862 col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb);
2863 }
2864 }
2865 else if (node->value.element.name[0] == '!' ||
2866 node->value.element.name[0] == '?')
2867 {
2868 /*
2869 * The ? and ! elements are special-cases...
2870 */
2871
2872 if ((*putc_cb)('>', p) < 0)
2873 return (-1);
2874 else
2875 col ++;
2876
2877 col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2878 }
2879 else
2880 {
2881 if ((*putc_cb)(' ', p) < 0)
2882 return (-1);
2883 if ((*putc_cb)('/', p) < 0)
2884 return (-1);
2885 if ((*putc_cb)('>', p) < 0)
2886 return (-1);
2887
2888 col += 3;
2889
2890 col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2891 }
2892 break;
2893
2894 case MXML_INTEGER :
2895 if (node->prev)
2896 {
2897 if (global->wrap > 0 && col > global->wrap)
2898 {
2899 if ((*putc_cb)('\n', p) < 0)
2900 return (-1);
2901
2902 col = 0;
2903 }
2904 else if ((*putc_cb)(' ', p) < 0)
2905 return (-1);
2906 else
2907 col ++;
2908 }
2909
2910 sprintf(s, "%d", node->value.integer);
2911 if (mxml_write_string(s, p, putc_cb) < 0)
2912 return (-1);
2913
2914 col += strlen(s);
2915 break;
2916
2917 case MXML_OPAQUE :
2918 if (mxml_write_string(node->value.opaque, p, putc_cb) < 0)
2919 return (-1);
2920
2921 col += strlen(node->value.opaque);
2922 break;
2923
2924 case MXML_REAL :
2925 if (node->prev)
2926 {
2927 if (global->wrap > 0 && col > global->wrap)
2928 {
2929 if ((*putc_cb)('\n', p) < 0)
2930 return (-1);
2931
2932 col = 0;
2933 }
2934 else if ((*putc_cb)(' ', p) < 0)
2935 return (-1);
2936 else
2937 col ++;
2938 }
2939
2940 sprintf(s, "%f", node->value.real);
2941 if (mxml_write_string(s, p, putc_cb) < 0)
2942 return (-1);
2943
2944 col += strlen(s);
2945 break;
2946
2947 case MXML_TEXT :
2948 if (node->value.text.whitespace && col > 0)
2949 {
2950 if (global->wrap > 0 && col > global->wrap)
2951 {
2952 if ((*putc_cb)('\n', p) < 0)
2953 return (-1);
2954
2955 col = 0;
2956 }
2957 else if ((*putc_cb)(' ', p) < 0)
2958 return (-1);
2959 else
2960 col ++;
2961 }
2962
2963 if (mxml_write_string(node->value.text.string, p, putc_cb) < 0)
2964 return (-1);
2965
2966 col += strlen(node->value.text.string);
2967 break;
2968
2969 case MXML_CUSTOM :
2970 if (global->custom_save_cb)
2971 {
2972 char *data; /* Custom data string */
2973 const char *newline; /* Last newline in string */
2974
2975
2976 if ((data = (*global->custom_save_cb)(node)) == NULL)
2977 return (-1);
2978
2979 if (mxml_write_string(data, p, putc_cb) < 0)
2980 return (-1);
2981
2982 if ((newline = strrchr(data, '\n')) == NULL)
2983 col += strlen(data);
2984 else
2985 col = strlen(newline);
2986
2987 free(data);
2988 break;
2989 }
2990
2991 default : /* Should never happen */
2992 return (-1);
2993 }
2994
2995 return (col);
2996}
2997
2998
2999/*
3000 * 'mxml_write_string()' - Write a string, escaping & and < as needed.
3001 */
3002
3003static int /* O - 0 on success, -1 on failure */
3004mxml_write_string(
3005 const char *s, /* I - String to write */
3006 void *p, /* I - Write pointer */
3007 _mxml_putc_cb_t putc_cb) /* I - Write callback */
3008{
3009 const char *name; /* Entity name, if any */
3010
3011
3012 while (*s)
3013 {
3014 if ((name = mxmlEntityGetName(*s)) != NULL)
3015 {
3016 if ((*putc_cb)('&', p) < 0)
3017 return (-1);
3018
3019 while (*name)
3020 {
3021 if ((*putc_cb)(*name, p) < 0)
3022 return (-1);
3023 name ++;
3024 }
3025
3026 if ((*putc_cb)(';', p) < 0)
3027 return (-1);
3028 }
3029 else if ((*putc_cb)(*s, p) < 0)
3030 return (-1);
3031
3032 s ++;
3033 }
3034
3035 return (0);
3036}
3037
3038
3039/*
3040 * 'mxml_write_ws()' - Do whitespace callback...
3041 */
3042
3043static int /* O - New column */
3044mxml_write_ws(mxml_node_t *node, /* I - Current node */
3045 void *p, /* I - Write pointer */
3046 mxml_save_cb_t cb, /* I - Callback function */
3047 int ws, /* I - Where value */
3048 int col, /* I - Current column */
3049 _mxml_putc_cb_t putc_cb) /* I - Write callback */
3050{
3051 const char *s; /* Whitespace string */
3052
3053
3054 if (cb && (s = (*cb)(node, ws)) != NULL)
3055 {
3056 while (*s)
3057 {
3058 if ((*putc_cb)(*s, p) < 0)
3059 return (-1);
3060 else if (*s == '\n')
3061 col = 0;
3062 else if (*s == '\t')
3063 {
3064 col += MXML_TAB;
3065 col = col - (col % MXML_TAB);
3066 }
3067 else
3068 col ++;
3069
3070 s ++;
3071 }
3072 }
3073
3074 return (col);
3075}
3076
3077
3078/*
3079 * End of "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $".
3080 */
diff --git a/pathologist/src/minixml/mxml-get.c b/pathologist/src/minixml/mxml-get.c
new file mode 100644
index 0000000..a5356d5
--- /dev/null
+++ b/pathologist/src/minixml/mxml-get.c
@@ -0,0 +1,471 @@
1/*
2 * "$Id: mxml-get.c 427 2011-01-03 02:03:29Z mike $"
3 *
4 * Node get functions for Mini-XML, a small XML-like file parsing library.
5 *
6 * Copyright 2011 by Michael R Sweet.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Michael R Sweet and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file. If this file is
12 * missing or damaged, see the license at:
13 *
14 * http://www.minixml.org/
15 *
16 * Contents:
17 *
18 * mxmlGetCDATA() - Get the value for a CDATA node.
19 * mxmlGetCustom() - Get the value for a custom node.
20 * mxmlGetElement() - Get the name for an element node.
21 * mxmlGetFirstChild() - Get the first child of an element node.
22 * mxmlGetInteger() - Get the integer value from the specified node or its
23 * first child.
24 * mxmlGetLastChild() - Get the last child of an element node.
25 * mxmlGetNextSibling() - Get the next node for the current parent.
26 * mxmlGetOpaque() - Get an opaque string value for a node or its first
27 * child.
28 * mxmlGetParent() - Get the parent node.
29 * mxmlGetPrevSibling() - Get the previous node for the current parent.
30 * mxmlGetReal() - Get the real value for a node or its first child.
31 * mxmlGetText() - Get the text value for a node or its first child.
32 * mxmlGetType() - Get the node type.
33 * mxmlGetUserData() - Get the user data pointer for a node.
34 */
35
36/*
37 * Include necessary headers...
38 */
39
40#include "config.h"
41#include "mxml.h"
42
43
44/*
45 * 'mxmlGetCDATA()' - Get the value for a CDATA node.
46 *
47 * @code NULL@ is returned if the node is not a CDATA element.
48 *
49 * @since Mini-XML 2.7@
50 */
51
52const char * /* O - CDATA value or NULL */
53mxmlGetCDATA(mxml_node_t *node) /* I - Node to get */
54{
55 /*
56 * Range check input...
57 */
58
59 if (!node || node->type != MXML_ELEMENT ||
60 strncmp(node->value.element.name, "![CDATA[", 8))
61 return (NULL);
62
63 /*
64 * Return the text following the CDATA declaration...
65 */
66
67 return (node->value.element.name + 8);
68}
69
70
71/*
72 * 'mxmlGetCustom()' - Get the value for a custom node.
73 *
74 * @code NULL@ is returned if the node (or its first child) is not a custom
75 * value node.
76 *
77 * @since Mini-XML 2.7@
78 */
79
80const void * /* O - Custom value or NULL */
81mxmlGetCustom(mxml_node_t *node) /* I - Node to get */
82{
83 /*
84 * Range check input...
85 */
86
87 if (!node)
88 return (NULL);
89
90 /*
91 * Return the integer value...
92 */
93
94 if (node->type == MXML_CUSTOM)
95 return (node->value.custom.data);
96 else if (node->type == MXML_ELEMENT &&
97 node->child &&
98 node->child->type == MXML_CUSTOM)
99 return (node->child->value.custom.data);
100 else
101 return (NULL);
102}
103
104
105/*
106 * 'mxmlGetElement()' - Get the name for an element node.
107 *
108 * @code NULL@ is returned if the node is not an element node.
109 *
110 * @since Mini-XML 2.7@
111 */
112
113const char * /* O - Element name or NULL */
114mxmlGetElement(mxml_node_t *node) /* I - Node to get */
115{
116 /*
117 * Range check input...
118 */
119
120 if (!node || node->type != MXML_ELEMENT)
121 return (NULL);
122
123 /*
124 * Return the element name...
125 */
126
127 return (node->value.element.name);
128}
129
130
131/*
132 * 'mxmlGetFirstChild()' - Get the first child of an element node.
133 *
134 * @code NULL@ is returned if the node is not an element node or if the node
135 * has no children.
136 *
137 * @since Mini-XML 2.7@
138 */
139
140mxml_node_t * /* O - First child or NULL */
141mxmlGetFirstChild(mxml_node_t *node) /* I - Node to get */
142{
143 /*
144 * Range check input...
145 */
146
147 if (!node || node->type != MXML_ELEMENT)
148 return (NULL);
149
150 /*
151 * Return the first child node...
152 */
153
154 return (node->child);
155}
156
157
158/*
159 * 'mxmlGetInteger()' - Get the integer value from the specified node or its
160 * first child.
161 *
162 * 0 is returned if the node (or its first child) is not an integer value node.
163 *
164 * @since Mini-XML 2.7@
165 */
166
167int /* O - Integer value or 0 */
168mxmlGetInteger(mxml_node_t *node) /* I - Node to get */
169{
170 /*
171 * Range check input...
172 */
173
174 if (!node)
175 return (0);
176
177 /*
178 * Return the integer value...
179 */
180
181 if (node->type == MXML_INTEGER)
182 return (node->value.integer);
183 else if (node->type == MXML_ELEMENT &&
184 node->child &&
185 node->child->type == MXML_INTEGER)
186 return (node->child->value.integer);
187 else
188 return (0);
189}
190
191
192/*
193 * 'mxmlGetLastChild()' - Get the last child of an element node.
194 *
195 * @code NULL@ is returned if the node is not an element node or if the node
196 * has no children.
197 *
198 * @since Mini-XML 2.7@
199 */
200
201mxml_node_t * /* O - Last child or NULL */
202mxmlGetLastChild(mxml_node_t *node) /* I - Node to get */
203{
204 /*
205 * Range check input...
206 */
207
208 if (!node || node->type != MXML_ELEMENT)
209 return (NULL);
210
211 /*
212 * Return the node type...
213 */
214
215 return (node->last_child);
216}
217
218
219/*
220 * 'mxmlGetNextSibling()' - Get the next node for the current parent.
221 *
222 * @code NULL@ is returned if this is the last child for the current parent.
223 *
224 * @since Mini-XML 2.7@
225 */
226
227mxml_node_t *
228mxmlGetNextSibling(mxml_node_t *node) /* I - Node to get */
229{
230 /*
231 * Range check input...
232 */
233
234 if (!node)
235 return (NULL);
236
237 /*
238 * Return the node type...
239 */
240
241 return (node->next);
242}
243
244
245/*
246 * 'mxmlGetOpaque()' - Get an opaque string value for a node or its first child.
247 *
248 * @code NULL@ is returned if the node (or its first child) is not an opaque
249 * value node.
250 *
251 * @since Mini-XML 2.7@
252 */
253
254const char * /* O - Opaque string or NULL */
255mxmlGetOpaque(mxml_node_t *node) /* I - Node to get */
256{
257 /*
258 * Range check input...
259 */
260
261 if (!node)
262 return (NULL);
263
264 /*
265 * Return the integer value...
266 */
267
268 if (node->type == MXML_OPAQUE)
269 return (node->value.opaque);
270 else if (node->type == MXML_ELEMENT &&
271 node->child &&
272 node->child->type == MXML_OPAQUE)
273 return (node->child->value.opaque);
274 else
275 return (NULL);
276}
277
278
279/*
280 * 'mxmlGetParent()' - Get the parent node.
281 *
282 * @code NULL@ is returned for a root node.
283 *
284 * @since Mini-XML 2.7@
285 */
286
287mxml_node_t * /* O - Parent node or NULL */
288mxmlGetParent(mxml_node_t *node) /* I - Node to get */
289{
290 /*
291 * Range check input...
292 */
293
294 if (!node)
295 return (NULL);
296
297 /*
298 * Return the node type...
299 */
300
301 return (node->parent);
302}
303
304
305/*
306 * 'mxmlGetPrevSibling()' - Get the previous node for the current parent.
307 *
308 * @code NULL@ is returned if this is the first child for the current parent.
309 *
310 * @since Mini-XML 2.7@
311 */
312
313mxml_node_t * /* O - Previous node or NULL */
314mxmlGetPrevSibling(mxml_node_t *node) /* I - Node to get */
315{
316 /*
317 * Range check input...
318 */
319
320 if (!node)
321 return (NULL);
322
323 /*
324 * Return the node type...
325 */
326
327 return (node->prev);
328}
329
330
331/*
332 * 'mxmlGetReal()' - Get the real value for a node or its first child.
333 *
334 * 0.0 is returned if the node (or its first child) is not a real value node.
335 *
336 * @since Mini-XML 2.7@
337 */
338
339double /* O - Real value or 0.0 */
340mxmlGetReal(mxml_node_t *node) /* I - Node to get */
341{
342 /*
343 * Range check input...
344 */
345
346 if (!node)
347 return (0.0);
348
349 /*
350 * Return the integer value...
351 */
352
353 if (node->type == MXML_REAL)
354 return (node->value.real);
355 else if (node->type == MXML_ELEMENT &&
356 node->child &&
357 node->child->type == MXML_REAL)
358 return (node->child->value.real);
359 else
360 return (0.0);
361}
362
363
364/*
365 * 'mxmlGetText()' - Get the text value for a node or its first child.
366 *
367 * @code NULL@ is returned if the node (or its first child) is not a text node.
368 * The "whitespace" argument can be NULL.
369 *
370 * @since Mini-XML 2.7@
371 */
372
373const char * /* O - Text string or NULL */
374mxmlGetText(mxml_node_t *node, /* I - Node to get */
375 int *whitespace) /* O - 1 if string is preceded by whitespace, 0 otherwise */
376{
377 /*
378 * Range check input...
379 */
380
381 if (!node)
382 {
383 if (whitespace)
384 *whitespace = 0;
385
386 return (NULL);
387 }
388
389 /*
390 * Return the integer value...
391 */
392
393 if (node->type == MXML_TEXT)
394 {
395 if (whitespace)
396 *whitespace = node->value.text.whitespace;
397
398 return (node->value.text.string);
399 }
400 else if (node->type == MXML_ELEMENT &&
401 node->child &&
402 node->child->type == MXML_TEXT)
403 {
404 if (whitespace)
405 *whitespace = node->child->value.text.whitespace;
406
407 return (node->child->value.text.string);
408 }
409 else
410 {
411 if (whitespace)
412 *whitespace = 0;
413
414 return (NULL);
415 }
416}
417
418
419/*
420 * 'mxmlGetType()' - Get the node type.
421 *
422 * @code MXML_IGNORE@ is returned if "node" is @code NULL@.
423 *
424 * @since Mini-XML 2.7@
425 */
426
427mxml_type_t /* O - Type of node */
428mxmlGetType(mxml_node_t *node) /* I - Node to get */
429{
430 /*
431 * Range check input...
432 */
433
434 if (!node)
435 return (MXML_IGNORE);
436
437 /*
438 * Return the node type...
439 */
440
441 return (node->type);
442}
443
444
445/*
446 * 'mxmlGetUserData()' - Get the user data pointer for a node.
447 *
448 * @since Mini-XML 2.7@
449 */
450
451void * /* O - User data pointer */
452mxmlGetUserData(mxml_node_t *node) /* I - Node to get */
453{
454 /*
455 * Range check input...
456 */
457
458 if (!node)
459 return (NULL);
460
461 /*
462 * Return the user data pointer...
463 */
464
465 return (node->user_data);
466}
467
468
469/*
470 * End of "$Id: mxml-get.c 427 2011-01-03 02:03:29Z mike $".
471 */
diff --git a/pathologist/src/minixml/mxml-index.c b/pathologist/src/minixml/mxml-index.c
new file mode 100644
index 0000000..b6efc66
--- /dev/null
+++ b/pathologist/src/minixml/mxml-index.c
@@ -0,0 +1,662 @@
1/*
2 * "$Id: mxml-index.c 426 2011-01-01 23:42:17Z mike $"
3 *
4 * Index support code for Mini-XML, a small XML-like file parsing library.
5 *
6 * Copyright 2003-2011 by Michael R Sweet.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Michael R Sweet and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file. If this file is
12 * missing or damaged, see the license at:
13 *
14 * http://www.minixml.org/
15 *
16 * Contents:
17 *
18 */
19
20/*
21 * Include necessary headers...
22 */
23
24#include "config.h"
25#include "mxml.h"
26
27
28/*
29 * Sort functions...
30 */
31
32static int index_compare(mxml_index_t *ind, mxml_node_t *first,
33 mxml_node_t *second);
34static int index_find(mxml_index_t *ind, const char *element,
35 const char *value, mxml_node_t *node);
36static void index_sort(mxml_index_t *ind, int left, int right);
37
38
39/*
40 * 'mxmlIndexDelete()' - Delete an index.
41 */
42
43void
44mxmlIndexDelete(mxml_index_t *ind) /* I - Index to delete */
45{
46 /*
47 * Range check input..
48 */
49
50 if (!ind)
51 return;
52
53 /*
54 * Free memory...
55 */
56
57 if (ind->attr)
58 free(ind->attr);
59
60 if (ind->alloc_nodes)
61 free(ind->nodes);
62
63 free(ind);
64}
65
66
67/*
68 * 'mxmlIndexEnum()' - Return the next node in the index.
69 *
70 * Nodes are returned in the sorted order of the index.
71 */
72
73mxml_node_t * /* O - Next node or NULL if there is none */
74mxmlIndexEnum(mxml_index_t *ind) /* I - Index to enumerate */
75{
76 /*
77 * Range check input...
78 */
79
80 if (!ind)
81 return (NULL);
82
83 /*
84 * Return the next node...
85 */
86
87 if (ind->cur_node < ind->num_nodes)
88 return (ind->nodes[ind->cur_node ++]);
89 else
90 return (NULL);
91}
92
93
94/*
95 * 'mxmlIndexFind()' - Find the next matching node.
96 *
97 * You should call mxmlIndexReset() prior to using this function for
98 * the first time with a particular set of "element" and "value"
99 * strings. Passing NULL for both "element" and "value" is equivalent
100 * to calling mxmlIndexEnum().
101 */
102
103mxml_node_t * /* O - Node or NULL if none found */
104mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */
105 const char *element, /* I - Element name to find, if any */
106 const char *value) /* I - Attribute value, if any */
107{
108 int diff, /* Difference between names */
109 current, /* Current entity in search */
110 first, /* First entity in search */
111 last; /* Last entity in search */
112
113
114#ifdef DEBUG
115 printf("mxmlIndexFind(ind=%p, element=\"%s\", value=\"%s\")\n",
116 ind, element ? element : "(null)", value ? value : "(null)");
117#endif /* DEBUG */
118
119 /*
120 * Range check input...
121 */
122
123 if (!ind || (!ind->attr && value))
124 {
125#ifdef DEBUG
126 puts(" returning NULL...");
127 printf(" ind->attr=\"%s\"\n", ind->attr ? ind->attr : "(null)");
128#endif /* DEBUG */
129
130 return (NULL);
131 }
132
133 /*
134 * If both element and value are NULL, just enumerate the nodes in the
135 * index...
136 */
137
138 if (!element && !value)
139 return (mxmlIndexEnum(ind));
140
141 /*
142 * If there are no nodes in the index, return NULL...
143 */
144
145 if (!ind->num_nodes)
146 {
147#ifdef DEBUG
148 puts(" returning NULL...");
149 puts(" no nodes!");
150#endif /* DEBUG */
151
152 return (NULL);
153 }
154
155 /*
156 * If cur_node == 0, then find the first matching node...
157 */
158
159 if (ind->cur_node == 0)
160 {
161 /*
162 * Find the first node using a modified binary search algorithm...
163 */
164
165 first = 0;
166 last = ind->num_nodes - 1;
167
168#ifdef DEBUG
169 printf(" find first time, num_nodes=%d...\n", ind->num_nodes);
170#endif /* DEBUG */
171
172 while ((last - first) > 1)
173 {
174 current = (first + last) / 2;
175
176#ifdef DEBUG
177 printf(" first=%d, last=%d, current=%d\n", first, last, current);
178#endif /* DEBUG */
179
180 if ((diff = index_find(ind, element, value, ind->nodes[current])) == 0)
181 {
182 /*
183 * Found a match, move back to find the first...
184 */
185
186#ifdef DEBUG
187 puts(" match!");
188#endif /* DEBUG */
189
190 while (current > 0 &&
191 !index_find(ind, element, value, ind->nodes[current - 1]))
192 current --;
193
194#ifdef DEBUG
195 printf(" returning first match=%d\n", current);
196#endif /* DEBUG */
197
198 /*
199 * Return the first match and save the index to the next...
200 */
201
202 ind->cur_node = current + 1;
203
204 return (ind->nodes[current]);
205 }
206 else if (diff < 0)
207 last = current;
208 else
209 first = current;
210
211#ifdef DEBUG
212 printf(" diff=%d\n", diff);
213#endif /* DEBUG */
214 }
215
216 /*
217 * If we get this far, then we found exactly 0 or 1 matches...
218 */
219
220 for (current = first; current <= last; current ++)
221 if (!index_find(ind, element, value, ind->nodes[current]))
222 {
223 /*
224 * Found exactly one (or possibly two) match...
225 */
226
227#ifdef DEBUG
228 printf(" returning only match %d...\n", current);
229#endif /* DEBUG */
230
231 ind->cur_node = current + 1;
232
233 return (ind->nodes[current]);
234 }
235
236 /*
237 * No matches...
238 */
239
240 ind->cur_node = ind->num_nodes;
241
242#ifdef DEBUG
243 puts(" returning NULL...");
244#endif /* DEBUG */
245
246 return (NULL);
247 }
248 else if (ind->cur_node < ind->num_nodes &&
249 !index_find(ind, element, value, ind->nodes[ind->cur_node]))
250 {
251 /*
252 * Return the next matching node...
253 */
254
255#ifdef DEBUG
256 printf(" returning next match %d...\n", ind->cur_node);
257#endif /* DEBUG */
258
259 return (ind->nodes[ind->cur_node ++]);
260 }
261
262 /*
263 * If we get this far, then we have no matches...
264 */
265
266 ind->cur_node = ind->num_nodes;
267
268#ifdef DEBUG
269 puts(" returning NULL...");
270#endif /* DEBUG */
271
272 return (NULL);
273}
274
275
276/*
277 * 'mxmlIndexGetCount()' - Get the number of nodes in an index.
278 *
279 * @since Mini-XML 2.7@
280 */
281
282int /* I - Number of nodes in index */
283mxmlIndexGetCount(mxml_index_t *ind) /* I - Index of nodes */
284{
285 /*
286 * Range check input...
287 */
288
289 if (!ind)
290 return (0);
291
292 /*
293 * Return the number of nodes in the index...
294 */
295
296 return (ind->num_nodes);
297}
298
299
300/*
301 * 'mxmlIndexNew()' - Create a new index.
302 *
303 * The index will contain all nodes that contain the named element and/or
304 * attribute. If both "element" and "attr" are NULL, then the index will
305 * contain a sorted list of the elements in the node tree. Nodes are
306 * sorted by element name and optionally by attribute value if the "attr"
307 * argument is not NULL.
308 */
309
310mxml_index_t * /* O - New index */
311mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */
312 const char *element, /* I - Element to index or NULL for all */
313 const char *attr) /* I - Attribute to index or NULL for none */
314{
315 mxml_index_t *ind; /* New index */
316 mxml_node_t *current, /* Current node in index */
317 **temp; /* Temporary node pointer array */
318
319
320 /*
321 * Range check input...
322 */
323
324#ifdef DEBUG
325 printf("mxmlIndexNew(node=%p, element=\"%s\", attr=\"%s\")\n",
326 node, element ? element : "(null)", attr ? attr : "(null)");
327#endif /* DEBUG */
328
329 if (!node)
330 return (NULL);
331
332 /*
333 * Create a new index...
334 */
335
336 if ((ind = calloc(1, sizeof(mxml_index_t))) == NULL)
337 {
338 mxml_error("Unable to allocate %d bytes for index - %s",
339 sizeof(mxml_index_t), strerror(errno));
340 return (NULL);
341 }
342
343 if (attr)
344 ind->attr = strdup(attr);
345
346 if (!element && !attr)
347 current = node;
348 else
349 current = mxmlFindElement(node, node, element, attr, NULL, MXML_DESCEND);
350
351 while (current)
352 {
353 if (ind->num_nodes >= ind->alloc_nodes)
354 {
355 if (!ind->alloc_nodes)
356 temp = malloc(64 * sizeof(mxml_node_t *));
357 else
358 temp = realloc(ind->nodes, (ind->alloc_nodes + 64) * sizeof(mxml_node_t *));
359
360 if (!temp)
361 {
362 /*
363 * Unable to allocate memory for the index, so abort...
364 */
365
366 mxml_error("Unable to allocate %d bytes for index: %s",
367 (ind->alloc_nodes + 64) * sizeof(mxml_node_t *),
368 strerror(errno));
369
370 mxmlIndexDelete(ind);
371 return (NULL);
372 }
373
374 ind->nodes = temp;
375 ind->alloc_nodes += 64;
376 }
377
378 ind->nodes[ind->num_nodes ++] = current;
379
380 current = mxmlFindElement(current, node, element, attr, NULL, MXML_DESCEND);
381 }
382
383 /*
384 * Sort nodes based upon the search criteria...
385 */
386
387#ifdef DEBUG
388 {
389 int i; /* Looping var */
390
391
392 printf("%d node(s) in index.\n\n", ind->num_nodes);
393
394 if (attr)
395 {
396 printf("Node Address Element %s\n", attr);
397 puts("-------- -------- -------------- ------------------------------");
398
399 for (i = 0; i < ind->num_nodes; i ++)
400 printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i],
401 ind->nodes[i]->value.element.name,
402 mxmlElementGetAttr(ind->nodes[i], attr));
403 }
404 else
405 {
406 puts("Node Address Element");
407 puts("-------- -------- --------------");
408
409 for (i = 0; i < ind->num_nodes; i ++)
410 printf("%8d %-8p %s\n", i, ind->nodes[i],
411 ind->nodes[i]->value.element.name);
412 }
413
414 putchar('\n');
415 }
416#endif /* DEBUG */
417
418 if (ind->num_nodes > 1)
419 index_sort(ind, 0, ind->num_nodes - 1);
420
421#ifdef DEBUG
422 {
423 int i; /* Looping var */
424
425
426 puts("After sorting:\n");
427
428 if (attr)
429 {
430 printf("Node Address Element %s\n", attr);
431 puts("-------- -------- -------------- ------------------------------");
432
433 for (i = 0; i < ind->num_nodes; i ++)
434 printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i],
435 ind->nodes[i]->value.element.name,
436 mxmlElementGetAttr(ind->nodes[i], attr));
437 }
438 else
439 {
440 puts("Node Address Element");
441 puts("-------- -------- --------------");
442
443 for (i = 0; i < ind->num_nodes; i ++)
444 printf("%8d %-8p %s\n", i, ind->nodes[i],
445 ind->nodes[i]->value.element.name);
446 }
447
448 putchar('\n');
449 }
450#endif /* DEBUG */
451
452 /*
453 * Return the new index...
454 */
455
456 return (ind);
457}
458
459
460/*
461 * 'mxmlIndexReset()' - Reset the enumeration/find pointer in the index and
462 * return the first node in the index.
463 *
464 * This function should be called prior to using mxmlIndexEnum() or
465 * mxmlIndexFind() for the first time.
466 */
467
468mxml_node_t * /* O - First node or NULL if there is none */
469mxmlIndexReset(mxml_index_t *ind) /* I - Index to reset */
470{
471#ifdef DEBUG
472 printf("mxmlIndexReset(ind=%p)\n", ind);
473#endif /* DEBUG */
474
475 /*
476 * Range check input...
477 */
478
479 if (!ind)
480 return (NULL);
481
482 /*
483 * Set the index to the first element...
484 */
485
486 ind->cur_node = 0;
487
488 /*
489 * Return the first node...
490 */
491
492 if (ind->num_nodes)
493 return (ind->nodes[0]);
494 else
495 return (NULL);
496}
497
498
499/*
500 * 'index_compare()' - Compare two nodes.
501 */
502
503static int /* O - Result of comparison */
504index_compare(mxml_index_t *ind, /* I - Index */
505 mxml_node_t *first, /* I - First node */
506 mxml_node_t *second) /* I - Second node */
507{
508 int diff; /* Difference */
509
510
511 /*
512 * Check the element name...
513 */
514
515 if ((diff = strcmp(first->value.element.name,
516 second->value.element.name)) != 0)
517 return (diff);
518
519 /*
520 * Check the attribute value...
521 */
522
523 if (ind->attr)
524 {
525 if ((diff = strcmp(mxmlElementGetAttr(first, ind->attr),
526 mxmlElementGetAttr(second, ind->attr))) != 0)
527 return (diff);
528 }
529
530 /*
531 * No difference, return 0...
532 */
533
534 return (0);
535}
536
537
538/*
539 * 'index_find()' - Compare a node with index values.
540 */
541
542static int /* O - Result of comparison */
543index_find(mxml_index_t *ind, /* I - Index */
544 const char *element, /* I - Element name or NULL */
545 const char *value, /* I - Attribute value or NULL */
546 mxml_node_t *node) /* I - Node */
547{
548 int diff; /* Difference */
549
550
551 /*
552 * Check the element name...
553 */
554
555 if (element)
556 {
557 if ((diff = strcmp(element, node->value.element.name)) != 0)
558 return (diff);
559 }
560
561 /*
562 * Check the attribute value...
563 */
564
565 if (value)
566 {
567 if ((diff = strcmp(value, mxmlElementGetAttr(node, ind->attr))) != 0)
568 return (diff);
569 }
570
571 /*
572 * No difference, return 0...
573 */
574
575 return (0);
576}
577
578
579/*
580 * 'index_sort()' - Sort the nodes in the index...
581 *
582 * This function implements the classic quicksort algorithm...
583 */
584
585static void
586index_sort(mxml_index_t *ind, /* I - Index to sort */
587 int left, /* I - Left node in partition */
588 int right) /* I - Right node in partition */
589{
590 mxml_node_t *pivot, /* Pivot node */
591 *temp; /* Swap node */
592 int templ, /* Temporary left node */
593 tempr; /* Temporary right node */
594
595
596 /*
597 * Loop until we have sorted all the way to the right...
598 */
599
600 do
601 {
602 /*
603 * Sort the pivot in the current partition...
604 */
605
606 pivot = ind->nodes[left];
607
608 for (templ = left, tempr = right; templ < tempr;)
609 {
610 /*
611 * Move left while left node <= pivot node...
612 */
613
614 while ((templ < right) &&
615 index_compare(ind, ind->nodes[templ], pivot) <= 0)
616 templ ++;
617
618 /*
619 * Move right while right node > pivot node...
620 */
621
622 while ((tempr > left) &&
623 index_compare(ind, ind->nodes[tempr], pivot) > 0)
624 tempr --;
625
626 /*
627 * Swap nodes if needed...
628 */
629
630 if (templ < tempr)
631 {
632 temp = ind->nodes[templ];
633 ind->nodes[templ] = ind->nodes[tempr];
634 ind->nodes[tempr] = temp;
635 }
636 }
637
638 /*
639 * When we get here, the right (tempr) node is the new position for the
640 * pivot node...
641 */
642
643 if (index_compare(ind, pivot, ind->nodes[tempr]) > 0)
644 {
645 ind->nodes[left] = ind->nodes[tempr];
646 ind->nodes[tempr] = pivot;
647 }
648
649 /*
650 * Recursively sort the left partition as needed...
651 */
652
653 if (left < (tempr - 1))
654 index_sort(ind, left, tempr - 1);
655 }
656 while (right > (left = tempr + 1));
657}
658
659
660/*
661 * End of "$Id: mxml-index.c 426 2011-01-01 23:42:17Z mike $".
662 */
diff --git a/pathologist/src/minixml/mxml-node.c b/pathologist/src/minixml/mxml-node.c
new file mode 100644
index 0000000..44af759
--- /dev/null
+++ b/pathologist/src/minixml/mxml-node.c
@@ -0,0 +1,807 @@
1/*
2 * "$Id: mxml-node.c 436 2011-01-22 01:02:05Z mike $"
3 *
4 * Node support code for Mini-XML, a small XML-like file parsing library.
5 *
6 * Copyright 2003-2011 by Michael R Sweet.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Michael R Sweet and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file. If this file is
12 * missing or damaged, see the license at:
13 *
14 * http://www.minixml.org/
15 *
16 * Contents:
17 *
18 * mxmlAdd() - Add a node to a tree.
19 * mxmlDelete() - Delete a node and all of its children.
20 * mxmlGetRefCount() - Get the current reference (use) count for a node.
21 * mxmlNewCDATA() - Create a new CDATA node.
22 * mxmlNewCustom() - Create a new custom data node.
23 * mxmlNewElement() - Create a new element node.
24 * mxmlNewInteger() - Create a new integer node.
25 * mxmlNewOpaque() - Create a new opaque string.
26 * mxmlNewReal() - Create a new real number node.
27 * mxmlNewText() - Create a new text fragment node.
28 * mxmlNewTextf() - Create a new formatted text fragment node.
29 * mxmlRemove() - Remove a node from its parent.
30 * mxmlNewXML() - Create a new XML document tree.
31 * mxmlRelease() - Release a node.
32 * mxmlRetain() - Retain a node.
33 * mxml_new() - Create a new node.
34 */
35
36/*
37 * Include necessary headers...
38 */
39
40#include "config.h"
41#include "mxml.h"
42
43
44/*
45 * Local functions...
46 */
47
48static mxml_node_t *mxml_new(mxml_node_t *parent, mxml_type_t type);
49
50
51/*
52 * 'mxmlAdd()' - Add a node to a tree.
53 *
54 * Adds the specified node to the parent. If the child argument is not
55 * NULL, puts the new node before or after the specified child depending
56 * on the value of the where argument. If the child argument is NULL,
57 * puts the new node at the beginning of the child list (MXML_ADD_BEFORE)
58 * or at the end of the child list (MXML_ADD_AFTER). The constant
59 * MXML_ADD_TO_PARENT can be used to specify a NULL child pointer.
60 */
61
62void
63mxmlAdd(mxml_node_t *parent, /* I - Parent node */
64 int where, /* I - Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER */
65 mxml_node_t *child, /* I - Child node for where or MXML_ADD_TO_PARENT */
66 mxml_node_t *node) /* I - Node to add */
67{
68#ifdef DEBUG
69 fprintf(stderr, "mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent,
70 where, child, node);
71#endif /* DEBUG */
72
73 /*
74 * Range check input...
75 */
76
77 if (!parent || !node)
78 return;
79
80#if DEBUG > 1
81 fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent);
82 if (parent)
83 {
84 fprintf(stderr, " BEFORE: parent->child=%p\n", parent->child);
85 fprintf(stderr, " BEFORE: parent->last_child=%p\n", parent->last_child);
86 fprintf(stderr, " BEFORE: parent->prev=%p\n", parent->prev);
87 fprintf(stderr, " BEFORE: parent->next=%p\n", parent->next);
88 }
89#endif /* DEBUG > 1 */
90
91 /*
92 * Remove the node from any existing parent...
93 */
94
95 if (node->parent)
96 mxmlRemove(node);
97
98 /*
99 * Reset pointers...
100 */
101
102 node->parent = parent;
103
104 switch (where)
105 {
106 case MXML_ADD_BEFORE :
107 if (!child || child == parent->child || child->parent != parent)
108 {
109 /*
110 * Insert as first node under parent...
111 */
112
113 node->next = parent->child;
114
115 if (parent->child)
116 parent->child->prev = node;
117 else
118 parent->last_child = node;
119
120 parent->child = node;
121 }
122 else
123 {
124 /*
125 * Insert node before this child...
126 */
127
128 node->next = child;
129 node->prev = child->prev;
130
131 if (child->prev)
132 child->prev->next = node;
133 else
134 parent->child = node;
135
136 child->prev = node;
137 }
138 break;
139
140 case MXML_ADD_AFTER :
141 if (!child || child == parent->last_child || child->parent != parent)
142 {
143 /*
144 * Insert as last node under parent...
145 */
146
147 node->parent = parent;
148 node->prev = parent->last_child;
149
150 if (parent->last_child)
151 parent->last_child->next = node;
152 else
153 parent->child = node;
154
155 parent->last_child = node;
156 }
157 else
158 {
159 /*
160 * Insert node after this child...
161 */
162
163 node->prev = child;
164 node->next = child->next;
165
166 if (child->next)
167 child->next->prev = node;
168 else
169 parent->last_child = node;
170
171 child->next = node;
172 }
173 break;
174 }
175
176#if DEBUG > 1
177 fprintf(stderr, " AFTER: node->parent=%p\n", node->parent);
178 if (parent)
179 {
180 fprintf(stderr, " AFTER: parent->child=%p\n", parent->child);
181 fprintf(stderr, " AFTER: parent->last_child=%p\n", parent->last_child);
182 fprintf(stderr, " AFTER: parent->prev=%p\n", parent->prev);
183 fprintf(stderr, " AFTER: parent->next=%p\n", parent->next);
184 }
185#endif /* DEBUG > 1 */
186}
187
188
189/*
190 * 'mxmlDelete()' - Delete a node and all of its children.
191 *
192 * If the specified node has a parent, this function first removes the
193 * node from its parent using the mxmlRemove() function.
194 */
195
196void
197mxmlDelete(mxml_node_t *node) /* I - Node to delete */
198{
199 int i; /* Looping var */
200
201
202#ifdef DEBUG
203 fprintf(stderr, "mxmlDelete(node=%p)\n", node);
204#endif /* DEBUG */
205
206 /*
207 * Range check input...
208 */
209
210 if (!node)
211 return;
212
213 /*
214 * Remove the node from its parent, if any...
215 */
216
217 mxmlRemove(node);
218
219 /*
220 * Delete children...
221 */
222
223 while (node->child)
224 mxmlDelete(node->child);
225
226 /*
227 * Now delete any node data...
228 */
229
230 switch (node->type)
231 {
232 case MXML_ELEMENT :
233 if (node->value.element.name)
234 free(node->value.element.name);
235
236 if (node->value.element.num_attrs)
237 {
238 for (i = 0; i < node->value.element.num_attrs; i ++)
239 {
240 if (node->value.element.attrs[i].name)
241 free(node->value.element.attrs[i].name);
242 if (node->value.element.attrs[i].value)
243 free(node->value.element.attrs[i].value);
244 }
245
246 free(node->value.element.attrs);
247 }
248 break;
249 case MXML_INTEGER :
250 /* Nothing to do */
251 break;
252 case MXML_OPAQUE :
253 if (node->value.opaque)
254 free(node->value.opaque);
255 break;
256 case MXML_REAL :
257 /* Nothing to do */
258 break;
259 case MXML_TEXT :
260 if (node->value.text.string)
261 free(node->value.text.string);
262 break;
263 case MXML_CUSTOM :
264 if (node->value.custom.data &&
265 node->value.custom.destroy)
266 (*(node->value.custom.destroy))(node->value.custom.data);
267 break;
268 default :
269 break;
270 }
271
272 /*
273 * Free this node...
274 */
275
276 free(node);
277}
278
279
280/*
281 * 'mxmlGetRefCount()' - Get the current reference (use) count for a node.
282 *
283 * The initial reference count of new nodes is 1. Use the @link mxmlRetain@
284 * and @link mxmlRelease@ functions to increment and decrement a node's
285 * reference count.
286 *
287 * @since Mini-XML 2.7@.
288 */
289
290int /* O - Reference count */
291mxmlGetRefCount(mxml_node_t *node) /* I - Node */
292{
293 /*
294 * Range check input...
295 */
296
297 if (!node)
298 return (0);
299
300 /*
301 * Return the reference count...
302 */
303
304 return (node->ref_count);
305}
306
307
308/*
309 * 'mxmlNewCDATA()' - Create a new CDATA node.
310 *
311 * The new CDATA node is added to the end of the specified parent's child
312 * list. The constant MXML_NO_PARENT can be used to specify that the new
313 * CDATA node has no parent. The data string must be nul-terminated and
314 * is copied into the new node. CDATA nodes use the MXML_ELEMENT type.
315 *
316 * @since Mini-XML 2.3@
317 */
318
319mxml_node_t * /* O - New node */
320mxmlNewCDATA(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
321 const char *data) /* I - Data string */
322{
323 mxml_node_t *node; /* New node */
324
325
326#ifdef DEBUG
327 fprintf(stderr, "mxmlNewCDATA(parent=%p, data=\"%s\")\n",
328 parent, data ? data : "(null)");
329#endif /* DEBUG */
330
331 /*
332 * Range check input...
333 */
334
335 if (!data)
336 return (NULL);
337
338 /*
339 * Create the node and set the name value...
340 */
341
342 if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
343 node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data);
344
345 return (node);
346}
347
348
349/*
350 * 'mxmlNewCustom()' - Create a new custom data node.
351 *
352 * The new custom node is added to the end of the specified parent's child
353 * list. The constant MXML_NO_PARENT can be used to specify that the new
354 * element node has no parent. NULL can be passed when the data in the
355 * node is not dynamically allocated or is separately managed.
356 *
357 * @since Mini-XML 2.1@
358 */
359
360mxml_node_t * /* O - New node */
361mxmlNewCustom(
362 mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
363 void *data, /* I - Pointer to data */
364 mxml_custom_destroy_cb_t destroy) /* I - Function to destroy data */
365{
366 mxml_node_t *node; /* New node */
367
368
369#ifdef DEBUG
370 fprintf(stderr, "mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent,
371 data, destroy);
372#endif /* DEBUG */
373
374 /*
375 * Create the node and set the value...
376 */
377
378 if ((node = mxml_new(parent, MXML_CUSTOM)) != NULL)
379 {
380 node->value.custom.data = data;
381 node->value.custom.destroy = destroy;
382 }
383
384 return (node);
385}
386
387
388/*
389 * 'mxmlNewElement()' - Create a new element node.
390 *
391 * The new element node is added to the end of the specified parent's child
392 * list. The constant MXML_NO_PARENT can be used to specify that the new
393 * element node has no parent.
394 */
395
396mxml_node_t * /* O - New node */
397mxmlNewElement(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
398 const char *name) /* I - Name of element */
399{
400 mxml_node_t *node; /* New node */
401
402
403#ifdef DEBUG
404 fprintf(stderr, "mxmlNewElement(parent=%p, name=\"%s\")\n", parent,
405 name ? name : "(null)");
406#endif /* DEBUG */
407
408 /*
409 * Range check input...
410 */
411
412 if (!name)
413 return (NULL);
414
415 /*
416 * Create the node and set the element name...
417 */
418
419 if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
420 node->value.element.name = strdup(name);
421
422 return (node);
423}
424
425
426/*
427 * 'mxmlNewInteger()' - Create a new integer node.
428 *
429 * The new integer node is added to the end of the specified parent's child
430 * list. The constant MXML_NO_PARENT can be used to specify that the new
431 * integer node has no parent.
432 */
433
434mxml_node_t * /* O - New node */
435mxmlNewInteger(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
436 int integer) /* I - Integer value */
437{
438 mxml_node_t *node; /* New node */
439
440
441#ifdef DEBUG
442 fprintf(stderr, "mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer);
443#endif /* DEBUG */
444
445 /*
446 * Create the node and set the element name...
447 */
448
449 if ((node = mxml_new(parent, MXML_INTEGER)) != NULL)
450 node->value.integer = integer;
451
452 return (node);
453}
454
455
456/*
457 * 'mxmlNewOpaque()' - Create a new opaque string.
458 *
459 * The new opaque node is added to the end of the specified parent's child
460 * list. The constant MXML_NO_PARENT can be used to specify that the new
461 * opaque node has no parent. The opaque string must be nul-terminated and
462 * is copied into the new node.
463 */
464
465mxml_node_t * /* O - New node */
466mxmlNewOpaque(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
467 const char *opaque) /* I - Opaque string */
468{
469 mxml_node_t *node; /* New node */
470
471
472#ifdef DEBUG
473 fprintf(stderr, "mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent,
474 opaque ? opaque : "(null)");
475#endif /* DEBUG */
476
477 /*
478 * Range check input...
479 */
480
481 if (!opaque)
482 return (NULL);
483
484 /*
485 * Create the node and set the element name...
486 */
487
488 if ((node = mxml_new(parent, MXML_OPAQUE)) != NULL)
489 node->value.opaque = strdup(opaque);
490
491 return (node);
492}
493
494
495/*
496 * 'mxmlNewReal()' - Create a new real number node.
497 *
498 * The new real number node is added to the end of the specified parent's
499 * child list. The constant MXML_NO_PARENT can be used to specify that
500 * the new real number node has no parent.
501 */
502
503mxml_node_t * /* O - New node */
504mxmlNewReal(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
505 double real) /* I - Real number value */
506{
507 mxml_node_t *node; /* New node */
508
509
510#ifdef DEBUG
511 fprintf(stderr, "mxmlNewReal(parent=%p, real=%g)\n", parent, real);
512#endif /* DEBUG */
513
514 /*
515 * Create the node and set the element name...
516 */
517
518 if ((node = mxml_new(parent, MXML_REAL)) != NULL)
519 node->value.real = real;
520
521 return (node);
522}
523
524
525/*
526 * 'mxmlNewText()' - Create a new text fragment node.
527 *
528 * The new text node is added to the end of the specified parent's child
529 * list. The constant MXML_NO_PARENT can be used to specify that the new
530 * text node has no parent. The whitespace parameter is used to specify
531 * whether leading whitespace is present before the node. The text
532 * string must be nul-terminated and is copied into the new node.
533 */
534
535mxml_node_t * /* O - New node */
536mxmlNewText(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
537 int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
538 const char *string) /* I - String */
539{
540 mxml_node_t *node; /* New node */
541
542
543#ifdef DEBUG
544 fprintf(stderr, "mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n",
545 parent, whitespace, string ? string : "(null)");
546#endif /* DEBUG */
547
548 /*
549 * Range check input...
550 */
551
552 if (!string)
553 return (NULL);
554
555 /*
556 * Create the node and set the text value...
557 */
558
559 if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
560 {
561 node->value.text.whitespace = whitespace;
562 node->value.text.string = strdup(string);
563 }
564
565 return (node);
566}
567
568
569/*
570 * 'mxmlNewTextf()' - Create a new formatted text fragment node.
571 *
572 * The new text node is added to the end of the specified parent's child
573 * list. The constant MXML_NO_PARENT can be used to specify that the new
574 * text node has no parent. The whitespace parameter is used to specify
575 * whether leading whitespace is present before the node. The format
576 * string must be nul-terminated and is formatted into the new node.
577 */
578
579mxml_node_t * /* O - New node */
580mxmlNewTextf(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
581 int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
582 const char *format, /* I - Printf-style frmat string */
583 ...) /* I - Additional args as needed */
584{
585 mxml_node_t *node; /* New node */
586 va_list ap; /* Pointer to arguments */
587
588
589#ifdef DEBUG
590 fprintf(stderr, "mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n",
591 parent, whitespace, format ? format : "(null)");
592#endif /* DEBUG */
593
594 /*
595 * Range check input...
596 */
597
598 if (!format)
599 return (NULL);
600
601 /*
602 * Create the node and set the text value...
603 */
604
605 if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
606 {
607 va_start(ap, format);
608
609 node->value.text.whitespace = whitespace;
610 node->value.text.string = _mxml_vstrdupf(format, ap);
611
612 va_end(ap);
613 }
614
615 return (node);
616}
617
618
619/*
620 * 'mxmlRemove()' - Remove a node from its parent.
621 *
622 * Does not free memory used by the node - use mxmlDelete() for that.
623 * This function does nothing if the node has no parent.
624 */
625
626void
627mxmlRemove(mxml_node_t *node) /* I - Node to remove */
628{
629#ifdef DEBUG
630 fprintf(stderr, "mxmlRemove(node=%p)\n", node);
631#endif /* DEBUG */
632
633 /*
634 * Range check input...
635 */
636
637 if (!node || !node->parent)
638 return;
639
640 /*
641 * Remove from parent...
642 */
643
644#if DEBUG > 1
645 fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent);
646 if (node->parent)
647 {
648 fprintf(stderr, " BEFORE: node->parent->child=%p\n", node->parent->child);
649 fprintf(stderr, " BEFORE: node->parent->last_child=%p\n", node->parent->last_child);
650 }
651 fprintf(stderr, " BEFORE: node->child=%p\n", node->child);
652 fprintf(stderr, " BEFORE: node->last_child=%p\n", node->last_child);
653 fprintf(stderr, " BEFORE: node->prev=%p\n", node->prev);
654 fprintf(stderr, " BEFORE: node->next=%p\n", node->next);
655#endif /* DEBUG > 1 */
656
657 if (node->prev)
658 node->prev->next = node->next;
659 else
660 node->parent->child = node->next;
661
662 if (node->next)
663 node->next->prev = node->prev;
664 else
665 node->parent->last_child = node->prev;
666
667 node->parent = NULL;
668 node->prev = NULL;
669 node->next = NULL;
670
671#if DEBUG > 1
672 fprintf(stderr, " AFTER: node->parent=%p\n", node->parent);
673 if (node->parent)
674 {
675 fprintf(stderr, " AFTER: node->parent->child=%p\n", node->parent->child);
676 fprintf(stderr, " AFTER: node->parent->last_child=%p\n", node->parent->last_child);
677 }
678 fprintf(stderr, " AFTER: node->child=%p\n", node->child);
679 fprintf(stderr, " AFTER: node->last_child=%p\n", node->last_child);
680 fprintf(stderr, " AFTER: node->prev=%p\n", node->prev);
681 fprintf(stderr, " AFTER: node->next=%p\n", node->next);
682#endif /* DEBUG > 1 */
683}
684
685
686/*
687 * 'mxmlNewXML()' - Create a new XML document tree.
688 *
689 * The "version" argument specifies the version number to put in the
690 * ?xml element node. If NULL, version 1.0 is assumed.
691 *
692 * @since Mini-XML 2.3@
693 */
694
695mxml_node_t * /* O - New ?xml node */
696mxmlNewXML(const char *version) /* I - Version number to use */
697{
698 char element[1024]; /* Element text */
699
700
701 snprintf(element, sizeof(element), "?xml version=\"%s\" encoding=\"utf-8\"?",
702 version ? version : "1.0");
703
704 return (mxmlNewElement(NULL, element));
705}
706
707
708/*
709 * 'mxmlRelease()' - Release a node.
710 *
711 * When the reference count reaches zero, the node (and any children)
712 * is deleted via mxmlDelete().
713 *
714 * @since Mini-XML 2.3@
715 */
716
717int /* O - New reference count */
718mxmlRelease(mxml_node_t *node) /* I - Node */
719{
720 if (node)
721 {
722 if ((-- node->ref_count) <= 0)
723 {
724 mxmlDelete(node);
725 return (0);
726 }
727 else
728 return (node->ref_count);
729 }
730 else
731 return (-1);
732}
733
734
735/*
736 * 'mxmlRetain()' - Retain a node.
737 *
738 * @since Mini-XML 2.3@
739 */
740
741int /* O - New reference count */
742mxmlRetain(mxml_node_t *node) /* I - Node */
743{
744 if (node)
745 return (++ node->ref_count);
746 else
747 return (-1);
748}
749
750
751/*
752 * 'mxml_new()' - Create a new node.
753 */
754
755static mxml_node_t * /* O - New node */
756mxml_new(mxml_node_t *parent, /* I - Parent node */
757 mxml_type_t type) /* I - Node type */
758{
759 mxml_node_t *node; /* New node */
760
761
762#if DEBUG > 1
763 fprintf(stderr, "mxml_new(parent=%p, type=%d)\n", parent, type);
764#endif /* DEBUG > 1 */
765
766 /*
767 * Allocate memory for the node...
768 */
769
770 if ((node = calloc(1, sizeof(mxml_node_t))) == NULL)
771 {
772#if DEBUG > 1
773 fputs(" returning NULL\n", stderr);
774#endif /* DEBUG > 1 */
775
776 return (NULL);
777 }
778
779#if DEBUG > 1
780 fprintf(stderr, " returning %p\n", node);
781#endif /* DEBUG > 1 */
782
783 /*
784 * Set the node type...
785 */
786
787 node->type = type;
788 node->ref_count = 1;
789
790 /*
791 * Add to the parent if present...
792 */
793
794 if (parent)
795 mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node);
796
797 /*
798 * Return the new node...
799 */
800
801 return (node);
802}
803
804
805/*
806 * End of "$Id: mxml-node.c 436 2011-01-22 01:02:05Z mike $".
807 */
diff --git a/pathologist/src/minixml/mxml-private.c b/pathologist/src/minixml/mxml-private.c
new file mode 100644
index 0000000..72f3e23
--- /dev/null
+++ b/pathologist/src/minixml/mxml-private.c
@@ -0,0 +1,331 @@
1/*
2 * "$Id: mxml-private.c 422 2010-11-07 22:55:11Z mike $"
3 *
4 * Private functions for Mini-XML, a small XML-like file parsing library.
5 *
6 * Copyright 2003-2010 by Michael R Sweet.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Michael R Sweet and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file. If this file is
12 * missing or damaged, see the license at:
13 *
14 * http://www.minixml.org/
15 *
16 * Contents:
17 *
18 * mxml_error() - Display an error message.
19 * mxml_integer_cb() - Default callback for integer values.
20 * mxml_opaque_cb() - Default callback for opaque values.
21 * mxml_real_cb() - Default callback for real number values.
22 * _mxml_global() - Get global data.
23 */
24
25/*
26 * Include necessary headers...
27 */
28
29#include "mxml-private.h"
30
31
32/*
33 * Some crazy people think that unloading a shared object is a good or safe
34 * thing to do. Unfortunately, most objects are simply *not* safe to unload
35 * and bad things *will* happen.
36 *
37 * The following mess of conditional code allows us to provide a destructor
38 * function in Mini-XML for our thread-global storage so that it can possibly
39 * be unloaded safely, although since there is no standard way to do so I
40 * can't even provide any guarantees that you can do it safely on all platforms.
41 *
42 * This code currently supports AIX, HP-UX, Linux, Mac OS X, Solaris, and
43 * Windows. It might work on the BSDs and IRIX, but I haven't tested that.
44 */
45
46#if defined(__sun) || defined(_AIX)
47# pragma fini(_mxml_fini)
48# define _MXML_FINI _mxml_fini
49#elif defined(__hpux)
50# pragma FINI _mxml_fini
51# define _MXML_FINI _mxml_fini
52#elif defined(__GNUC__) /* Linux and Mac OS X */
53# define _MXML_FINI __attribute((destructor)) _mxml_fini
54#else
55# define _MXML_FINI _fini
56#endif /* __sun */
57
58
59/*
60 * 'mxml_error()' - Display an error message.
61 */
62
63void
64mxml_error(const char *format, /* I - Printf-style format string */
65 ...) /* I - Additional arguments as needed */
66{
67 va_list ap; /* Pointer to arguments */
68 char s[1024]; /* Message string */
69 _mxml_global_t *global = _mxml_global();
70 /* Global data */
71
72
73 /*
74 * Range check input...
75 */
76
77 if (!format)
78 return;
79
80 /*
81 * Format the error message string...
82 */
83
84 va_start(ap, format);
85
86 vsnprintf(s, sizeof(s), format, ap);
87
88 va_end(ap);
89
90 /*
91 * And then display the error message...
92 */
93
94 if (global->error_cb)
95 (*global->error_cb)(s);
96 else
97 fprintf(stderr, "mxml: %s\n", s);
98}
99
100
101/*
102 * 'mxml_ignore_cb()' - Default callback for ignored values.
103 */
104
105mxml_type_t /* O - Node type */
106mxml_ignore_cb(mxml_node_t *node) /* I - Current node */
107{
108 (void)node;
109
110 return (MXML_IGNORE);
111}
112
113
114/*
115 * 'mxml_integer_cb()' - Default callback for integer values.
116 */
117
118mxml_type_t /* O - Node type */
119mxml_integer_cb(mxml_node_t *node) /* I - Current node */
120{
121 (void)node;
122
123 return (MXML_INTEGER);
124}
125
126
127/*
128 * 'mxml_opaque_cb()' - Default callback for opaque values.
129 */
130
131mxml_type_t /* O - Node type */
132mxml_opaque_cb(mxml_node_t *node) /* I - Current node */
133{
134 (void)node;
135
136 return (MXML_OPAQUE);
137}
138
139
140/*
141 * 'mxml_real_cb()' - Default callback for real number values.
142 */
143
144mxml_type_t /* O - Node type */
145mxml_real_cb(mxml_node_t *node) /* I - Current node */
146{
147 (void)node;
148
149 return (MXML_REAL);
150}
151
152
153#ifdef HAVE_PTHREAD_H /**** POSIX threading ****/
154# include <pthread.h>
155
156static pthread_key_t _mxml_key = -1; /* Thread local storage key */
157static pthread_once_t _mxml_key_once = PTHREAD_ONCE_INIT;
158 /* One-time initialization object */
159static void _mxml_init(void);
160static void _mxml_destructor(void *g);
161
162
163/*
164 * '_mxml_destructor()' - Free memory used for globals...
165 */
166
167static void
168_mxml_destructor(void *g) /* I - Global data */
169{
170 free(g);
171}
172
173
174/*
175 * '_mxml_fini()' - Clean up when unloaded.
176 */
177
178static void
179_MXML_FINI(void)
180{
181 _mxml_global_t *global; /* Global data */
182
183
184 if (_mxml_key != -1)
185 {
186 if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) != NULL)
187 _mxml_destructor(global);
188
189 pthread_key_delete(_mxml_key);
190 _mxml_key = -1;
191 }
192}
193
194
195/*
196 * '_mxml_global()' - Get global data.
197 */
198
199_mxml_global_t * /* O - Global data */
200_mxml_global(void)
201{
202 _mxml_global_t *global; /* Global data */
203
204
205 pthread_once(&_mxml_key_once, _mxml_init);
206
207 if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) == NULL)
208 {
209 global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t));
210 pthread_setspecific(_mxml_key, global);
211
212 global->num_entity_cbs = 1;
213 global->entity_cbs[0] = _mxml_entity_cb;
214 global->wrap = 72;
215 }
216
217 return (global);
218}
219
220
221/*
222 * '_mxml_init()' - Initialize global data...
223 */
224
225static void
226_mxml_init(void)
227{
228 pthread_key_create(&_mxml_key, _mxml_destructor);
229}
230
231
232#elif defined(WIN32) && defined(MXML1_EXPORTS) /**** WIN32 threading ****/
233# include <windows.h>
234
235static DWORD _mxml_tls_index; /* Index for global storage */
236
237
238/*
239 * 'DllMain()' - Main entry for library.
240 */
241
242BOOL WINAPI /* O - Success/failure */
243DllMain(HINSTANCE hinst, /* I - DLL module handle */
244 DWORD reason, /* I - Reason */
245 LPVOID reserved) /* I - Unused */
246{
247 _mxml_global_t *global; /* Global data */
248
249
250 (void)hinst;
251 (void)reserved;
252
253 switch (reason)
254 {
255 case DLL_PROCESS_ATTACH : /* Called on library initialization */
256 if ((_mxml_tls_index = TlsAlloc()) == TLS_OUT_OF_INDEXES)
257 return (FALSE);
258 break;
259
260 case DLL_THREAD_DETACH : /* Called when a thread terminates */
261 if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL)
262 free(global);
263 break;
264
265 case DLL_PROCESS_DETACH : /* Called when library is unloaded */
266 if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL)
267 free(global);
268
269 TlsFree(_mxml_tls_index);
270 break;
271
272 default:
273 break;
274 }
275
276 return (TRUE);
277}
278
279
280/*
281 * '_mxml_global()' - Get global data.
282 */
283
284_mxml_global_t * /* O - Global data */
285_mxml_global(void)
286{
287 _mxml_global_t *global; /* Global data */
288
289
290 if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) == NULL)
291 {
292 global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t));
293
294 global->num_entity_cbs = 1;
295 global->entity_cbs[0] = _mxml_entity_cb;
296 global->wrap = 72;
297
298 TlsSetValue(_mxml_tls_index, (LPVOID)global);
299 }
300
301 return (global);
302}
303
304
305#else /**** No threading ****/
306/*
307 * '_mxml_global()' - Get global data.
308 */
309
310_mxml_global_t * /* O - Global data */
311_mxml_global(void)
312{
313 static _mxml_global_t global = /* Global data */
314 {
315 NULL, /* error_cb */
316 1, /* num_entity_cbs */
317 { _mxml_entity_cb }, /* entity_cbs */
318 72, /* wrap */
319 NULL, /* custom_load_cb */
320 NULL /* custom_save_cb */
321 };
322
323
324 return (&global);
325}
326#endif /* HAVE_PTHREAD_H */
327
328
329/*
330 * End of "$Id: mxml-private.c 422 2010-11-07 22:55:11Z mike $".
331 */
diff --git a/pathologist/src/minixml/mxml-private.h b/pathologist/src/minixml/mxml-private.h
new file mode 100644
index 0000000..8789e6c
--- /dev/null
+++ b/pathologist/src/minixml/mxml-private.h
@@ -0,0 +1,50 @@
1/*
2 * "$Id: mxml-private.h 408 2010-09-19 05:26:46Z mike $"
3 *
4 * Private definitions for Mini-XML, a small XML-like file parsing library.
5 *
6 * Copyright 2003-2010 by Michael R Sweet.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Michael R Sweet and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file. If this file is
12 * missing or damaged, see the license at:
13 *
14 * http://www.minixml.org/
15 */
16
17/*
18 * Include necessary headers...
19 */
20
21#include "config.h"
22#include "mxml.h"
23
24
25/*
26 * Global, per-thread data...
27 */
28
29typedef struct _mxml_global_s
30{
31 void (*error_cb)(const char *);
32 int num_entity_cbs;
33 int (*entity_cbs[100])(const char *name);
34 int wrap;
35 mxml_custom_load_cb_t custom_load_cb;
36 mxml_custom_save_cb_t custom_save_cb;
37} _mxml_global_t;
38
39
40/*
41 * Functions...
42 */
43
44extern _mxml_global_t *_mxml_global(void);
45extern int _mxml_entity_cb(const char *name);
46
47
48/*
49 * End of "$Id: mxml-private.h 408 2010-09-19 05:26:46Z mike $".
50 */
diff --git a/pathologist/src/minixml/mxml-search.c b/pathologist/src/minixml/mxml-search.c
new file mode 100644
index 0000000..f975af1
--- /dev/null
+++ b/pathologist/src/minixml/mxml-search.c
@@ -0,0 +1,287 @@
1/*
2 * "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $"
3 *
4 * Search/navigation functions for Mini-XML, a small XML-like file
5 * parsing library.
6 *
7 * Copyright 2003-2010 by Michael R Sweet.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Michael R Sweet and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "COPYING"
12 * which should have been included with this file. If this file is
13 * missing or damaged, see the license at:
14 *
15 * http://www.minixml.org/
16 *
17 * Contents:
18 *
19 * mxmlFindElement() - Find the named element.
20 * mxmlFindValue() - Find a value with the given path.
21 * mxmlWalkNext() - Walk to the next logical node in the tree.
22 * mxmlWalkPrev() - Walk to the previous logical node in the tree.
23 */
24
25/*
26 * Include necessary headers...
27 */
28
29#include "config.h"
30#include "mxml.h"
31
32
33/*
34 * 'mxmlFindElement()' - Find the named element.
35 *
36 * The search is constrained by the name, attribute name, and value; any
37 * NULL names or values are treated as wildcards, so different kinds of
38 * searches can be implemented by looking for all elements of a given name
39 * or all elements with a specific attribute. The descend argument determines
40 * whether the search descends into child nodes; normally you will use
41 * MXML_DESCEND_FIRST for the initial search and MXML_NO_DESCEND to find
42 * additional direct descendents of the node. The top node argument
43 * constrains the search to a particular node's children.
44 */
45
46mxml_node_t * /* O - Element node or NULL */
47mxmlFindElement(mxml_node_t *node, /* I - Current node */
48 mxml_node_t *top, /* I - Top node */
49 const char *name, /* I - Element name or NULL for any */
50 const char *attr, /* I - Attribute name, or NULL for none */
51 const char *value, /* I - Attribute value, or NULL for any */
52 int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */
53{
54 const char *temp; /* Current attribute value */
55
56
57 /*
58 * Range check input...
59 */
60
61 if (!node || !top || (!attr && value))
62 return (NULL);
63
64 /*
65 * Start with the next node...
66 */
67
68 node = mxmlWalkNext(node, top, descend);
69
70 /*
71 * Loop until we find a matching element...
72 */
73
74 while (node != NULL)
75 {
76 /*
77 * See if this node matches...
78 */
79
80 if (node->type == MXML_ELEMENT &&
81 node->value.element.name &&
82 (!name || !strcmp(node->value.element.name, name)))
83 {
84 /*
85 * See if we need to check for an attribute...
86 */
87
88 if (!attr)
89 return (node); /* No attribute search, return it... */
90
91 /*
92 * Check for the attribute...
93 */
94
95 if ((temp = mxmlElementGetAttr(node, attr)) != NULL)
96 {
97 /*
98 * OK, we have the attribute, does it match?
99 */
100
101 if (!value || !strcmp(value, temp))
102 return (node); /* Yes, return it... */
103 }
104 }
105
106 /*
107 * No match, move on to the next node...
108 */
109
110 if (descend == MXML_DESCEND)
111 node = mxmlWalkNext(node, top, MXML_DESCEND);
112 else
113 node = node->next;
114 }
115
116 return (NULL);
117}
118
119
120/*
121 * 'mxmlFindPath()' - Find a node with the given path.
122 *
123 * The "path" is a slash-separated list of element names. The name "*" is
124 * considered a wildcard for one or more levels of elements. For example,
125 * "foo/one/two", "bar/two/one", "*\/one", and so forth.
126 *
127 * The first child node of the found node is returned if the given node has
128 * children and the first child is a value node.
129 *
130 * @since Mini-XML 2.7@
131 */
132
133mxml_node_t * /* O - Found node or NULL */
134mxmlFindPath(mxml_node_t *top, /* I - Top node */
135 const char *path) /* I - Path to element */
136{
137 mxml_node_t *node; /* Current node */
138 char element[256]; /* Current element name */
139 const char *pathsep; /* Separator in path */
140 int descend; /* mxmlFindElement option */
141
142
143 /*
144 * Range check input...
145 */
146
147 if (!top || !path || !*path)
148 return (NULL);
149
150 /*
151 * Search each element in the path...
152 */
153
154 node = top;
155 while (*path)
156 {
157 /*
158 * Handle wildcards...
159 */
160
161 if (!strncmp(path, "*/", 2))
162 {
163 path += 2;
164 descend = MXML_DESCEND;
165 }
166 else
167 descend = MXML_DESCEND_FIRST;
168
169 /*
170 * Get the next element in the path...
171 */
172
173 if ((pathsep = strchr(path, '/')) == NULL)
174 pathsep = path + strlen(path);
175
176 if (pathsep == path || (pathsep - path) >= sizeof(element))
177 return (NULL);
178
179 memcpy(element, path, pathsep - path);
180 element[pathsep - path] = '\0';
181
182 if (*pathsep)
183 path = pathsep + 1;
184 else
185 path = pathsep;
186
187 /*
188 * Search for the element...
189 */
190
191 if ((node = mxmlFindElement(node, node, element, NULL, NULL,
192 descend)) == NULL)
193 return (NULL);
194 }
195
196 /*
197 * If we get this far, return the node or its first child...
198 */
199
200 if (node->child && node->child->type != MXML_ELEMENT)
201 return (node->child);
202 else
203 return (node);
204}
205
206
207/*
208 * 'mxmlWalkNext()' - Walk to the next logical node in the tree.
209 *
210 * The descend argument controls whether the first child is considered
211 * to be the next node. The top node argument constrains the walk to
212 * the node's children.
213 */
214
215mxml_node_t * /* O - Next node or NULL */
216mxmlWalkNext(mxml_node_t *node, /* I - Current node */
217 mxml_node_t *top, /* I - Top node */
218 int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */
219{
220 if (!node)
221 return (NULL);
222 else if (node->child && descend)
223 return (node->child);
224 else if (node == top)
225 return (NULL);
226 else if (node->next)
227 return (node->next);
228 else if (node->parent && node->parent != top)
229 {
230 node = node->parent;
231
232 while (!node->next)
233 if (node->parent == top || !node->parent)
234 return (NULL);
235 else
236 node = node->parent;
237
238 return (node->next);
239 }
240 else
241 return (NULL);
242}
243
244
245/*
246 * 'mxmlWalkPrev()' - Walk to the previous logical node in the tree.
247 *
248 * The descend argument controls whether the previous node's last child
249 * is considered to be the previous node. The top node argument constrains
250 * the walk to the node's children.
251 */
252
253mxml_node_t * /* O - Previous node or NULL */
254mxmlWalkPrev(mxml_node_t *node, /* I - Current node */
255 mxml_node_t *top, /* I - Top node */
256 int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */
257{
258 if (!node || node == top)
259 return (NULL);
260 else if (node->prev)
261 {
262 if (node->prev->last_child && descend)
263 {
264 /*
265 * Find the last child under the previous node...
266 */
267
268 node = node->prev->last_child;
269
270 while (node->last_child)
271 node = node->last_child;
272
273 return (node);
274 }
275 else
276 return (node->prev);
277 }
278 else if (node->parent != top)
279 return (node->parent);
280 else
281 return (NULL);
282}
283
284
285/*
286 * End of "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $".
287 */
diff --git a/pathologist/src/minixml/mxml-set.c b/pathologist/src/minixml/mxml-set.c
new file mode 100644
index 0000000..b0bd527
--- /dev/null
+++ b/pathologist/src/minixml/mxml-set.c
@@ -0,0 +1,349 @@
1/*
2 * "$Id: mxml-set.c 441 2011-12-09 23:49:00Z mike $"
3 *
4 * Node set functions for Mini-XML, a small XML-like file parsing library.
5 *
6 * Copyright 2003-2011 by Michael R Sweet.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Michael R Sweet and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file. If this file is
12 * missing or damaged, see the license at:
13 *
14 * http://www.minixml.org/
15 *
16 * Contents:
17 *
18 * mxmlSetCDATA() - Set the element name of a CDATA node.
19 * mxmlSetCustom() - Set the data and destructor of a custom data node.
20 * mxmlSetElement() - Set the name of an element node.
21 * mxmlSetInteger() - Set the value of an integer node.
22 * mxmlSetOpaque() - Set the value of an opaque node.
23 * mxmlSetReal() - Set the value of a real number node.
24 * mxmlSetText() - Set the value of a text node.
25 * mxmlSetTextf() - Set the value of a text node to a formatted string.
26 * mxmlSetUserData() - Set the user data pointer for a node.
27 */
28
29/*
30 * Include necessary headers...
31 */
32
33#include "config.h"
34#include "mxml.h"
35
36
37/*
38 * 'mxmlSetCDATA()' - Set the element name of a CDATA node.
39 *
40 * The node is not changed if it (or its first child) is not a CDATA element node.
41 *
42 * @since Mini-XML 2.3@
43 */
44
45int /* O - 0 on success, -1 on failure */
46mxmlSetCDATA(mxml_node_t *node, /* I - Node to set */
47 const char *data) /* I - New data string */
48{
49 /*
50 * Range check input...
51 */
52
53 if (node && node->type == MXML_ELEMENT &&
54 strncmp(node->value.element.name, "![CDATA[", 8) &&
55 node->child && node->child->type == MXML_ELEMENT &&
56 !strncmp(node->child->value.element.name, "![CDATA[", 8))
57 node = node->child;
58
59 if (!node || node->type != MXML_ELEMENT || !data ||
60 strncmp(node->value.element.name, "![CDATA[", 8))
61 return (-1);
62
63 /*
64 * Free any old element value and set the new value...
65 */
66
67 if (node->value.element.name)
68 free(node->value.element.name);
69
70 node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data);
71
72 return (0);
73}
74
75
76/*
77 * 'mxmlSetCustom()' - Set the data and destructor of a custom data node.
78 *
79 * The node is not changed if it (or its first child) is not a custom node.
80 *
81 * @since Mini-XML 2.1@
82 */
83
84int /* O - 0 on success, -1 on failure */
85mxmlSetCustom(
86 mxml_node_t *node, /* I - Node to set */
87 void *data, /* I - New data pointer */
88 mxml_custom_destroy_cb_t destroy) /* I - New destructor function */
89{
90 /*
91 * Range check input...
92 */
93
94 if (node && node->type == MXML_ELEMENT &&
95 node->child && node->child->type == MXML_CUSTOM)
96 node = node->child;
97
98 if (!node || node->type != MXML_CUSTOM)
99 return (-1);
100
101 /*
102 * Free any old element value and set the new value...
103 */
104
105 if (node->value.custom.data && node->value.custom.destroy)
106 (*(node->value.custom.destroy))(node->value.custom.data);
107
108 node->value.custom.data = data;
109 node->value.custom.destroy = destroy;
110
111 return (0);
112}
113
114
115/*
116 * 'mxmlSetElement()' - Set the name of an element node.
117 *
118 * The node is not changed if it is not an element node.
119 */
120
121int /* O - 0 on success, -1 on failure */
122mxmlSetElement(mxml_node_t *node, /* I - Node to set */
123 const char *name) /* I - New name string */
124{
125 /*
126 * Range check input...
127 */
128
129 if (!node || node->type != MXML_ELEMENT || !name)
130 return (-1);
131
132 /*
133 * Free any old element value and set the new value...
134 */
135
136 if (node->value.element.name)
137 free(node->value.element.name);
138
139 node->value.element.name = strdup(name);
140
141 return (0);
142}
143
144
145/*
146 * 'mxmlSetInteger()' - Set the value of an integer node.
147 *
148 * The node is not changed if it (or its first child) is not an integer node.
149 */
150
151int /* O - 0 on success, -1 on failure */
152mxmlSetInteger(mxml_node_t *node, /* I - Node to set */
153 int integer) /* I - Integer value */
154{
155 /*
156 * Range check input...
157 */
158
159 if (node && node->type == MXML_ELEMENT &&
160 node->child && node->child->type == MXML_INTEGER)
161 node = node->child;
162
163 if (!node || node->type != MXML_INTEGER)
164 return (-1);
165
166 /*
167 * Set the new value and return...
168 */
169
170 node->value.integer = integer;
171
172 return (0);
173}
174
175
176/*
177 * 'mxmlSetOpaque()' - Set the value of an opaque node.
178 *
179 * The node is not changed if it (or its first child) is not an opaque node.
180 */
181
182int /* O - 0 on success, -1 on failure */
183mxmlSetOpaque(mxml_node_t *node, /* I - Node to set */
184 const char *opaque) /* I - Opaque string */
185{
186 /*
187 * Range check input...
188 */
189
190 if (node && node->type == MXML_ELEMENT &&
191 node->child && node->child->type == MXML_OPAQUE)
192 node = node->child;
193
194 if (!node || node->type != MXML_OPAQUE || !opaque)
195 return (-1);
196
197 /*
198 * Free any old opaque value and set the new value...
199 */
200
201 if (node->value.opaque)
202 free(node->value.opaque);
203
204 node->value.opaque = strdup(opaque);
205
206 return (0);
207}
208
209
210/*
211 * 'mxmlSetReal()' - Set the value of a real number node.
212 *
213 * The node is not changed if it (or its first child) is not a real number node.
214 */
215
216int /* O - 0 on success, -1 on failure */
217mxmlSetReal(mxml_node_t *node, /* I - Node to set */
218 double real) /* I - Real number value */
219{
220 /*
221 * Range check input...
222 */
223
224 if (node && node->type == MXML_ELEMENT &&
225 node->child && node->child->type == MXML_REAL)
226 node = node->child;
227
228 if (!node || node->type != MXML_REAL)
229 return (-1);
230
231 /*
232 * Set the new value and return...
233 */
234
235 node->value.real = real;
236
237 return (0);
238}
239
240
241/*
242 * 'mxmlSetText()' - Set the value of a text node.
243 *
244 * The node is not changed if it (or its first child) is not a text node.
245 */
246
247int /* O - 0 on success, -1 on failure */
248mxmlSetText(mxml_node_t *node, /* I - Node to set */
249 int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
250 const char *string) /* I - String */
251{
252 /*
253 * Range check input...
254 */
255
256 if (node && node->type == MXML_ELEMENT &&
257 node->child && node->child->type == MXML_TEXT)
258 node = node->child;
259
260 if (!node || node->type != MXML_TEXT || !string)
261 return (-1);
262
263 /*
264 * Free any old string value and set the new value...
265 */
266
267 if (node->value.text.string)
268 free(node->value.text.string);
269
270 node->value.text.whitespace = whitespace;
271 node->value.text.string = strdup(string);
272
273 return (0);
274}
275
276
277/*
278 * 'mxmlSetTextf()' - Set the value of a text node to a formatted string.
279 *
280 * The node is not changed if it (or its first child) is not a text node.
281 */
282
283int /* O - 0 on success, -1 on failure */
284mxmlSetTextf(mxml_node_t *node, /* I - Node to set */
285 int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
286 const char *format, /* I - Printf-style format string */
287 ...) /* I - Additional arguments as needed */
288{
289 va_list ap; /* Pointer to arguments */
290
291
292 /*
293 * Range check input...
294 */
295
296 if (node && node->type == MXML_ELEMENT &&
297 node->child && node->child->type == MXML_TEXT)
298 node = node->child;
299
300 if (!node || node->type != MXML_TEXT || !format)
301 return (-1);
302
303 /*
304 * Free any old string value and set the new value...
305 */
306
307 if (node->value.text.string)
308 free(node->value.text.string);
309
310 va_start(ap, format);
311
312 node->value.text.whitespace = whitespace;
313 node->value.text.string = _mxml_strdupf(format, ap);
314
315 va_end(ap);
316
317 return (0);
318}
319
320
321/*
322 * 'mxmlSetUserData()' - Set the user data pointer for a node.
323 *
324 * @since Mini-XML 2.7@
325 */
326
327int /* O - 0 on success, -1 on failure */
328mxmlSetUserData(mxml_node_t *node, /* I - Node to set */
329 void *data) /* I - User data pointer */
330{
331 /*
332 * Range check input...
333 */
334
335 if (!node)
336 return (-1);
337
338 /*
339 * Set the user data pointer and return...
340 */
341
342 node->user_data = data;
343 return (0);
344}
345
346
347/*
348 * End of "$Id: mxml-set.c 441 2011-12-09 23:49:00Z mike $".
349 */
diff --git a/pathologist/src/minixml/mxml-string.c b/pathologist/src/minixml/mxml-string.c
new file mode 100644
index 0000000..6be4252
--- /dev/null
+++ b/pathologist/src/minixml/mxml-string.c
@@ -0,0 +1,476 @@
1/*
2 * "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $"
3 *
4 * String functions for Mini-XML, a small XML-like file parsing library.
5 *
6 * Copyright 2003-2010 by Michael R Sweet.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Michael R Sweet and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file. If this file is
12 * missing or damaged, see the license at:
13 *
14 * http://www.minixml.org/
15 *
16 * Contents:
17 *
18 * _mxml_snprintf() - Format a string.
19 * _mxml_strdup() - Duplicate a string.
20 * _mxml_strdupf() - Format and duplicate a string.
21 * _mxml_vsnprintf() - Format a string into a fixed size buffer.
22 * _mxml_vstrdupf() - Format and duplicate a string.
23 */
24
25/*
26 * Include necessary headers...
27 */
28
29#include "config.h"
30
31
32/*
33 * The va_copy macro is part of C99, but many compilers don't implement it.
34 * Provide a "direct assignment" implmentation when va_copy isn't defined...
35 */
36
37#ifndef va_copy
38# ifdef __va_copy
39# define va_copy(dst,src) __va_copy(dst,src)
40# else
41# define va_copy(dst,src) memcpy(&dst, &src, sizeof(va_list))
42# endif /* __va_copy */
43#endif /* va_copy */
44
45
46#ifndef HAVE_SNPRINTF
47/*
48 * '_mxml_snprintf()' - Format a string.
49 */
50
51int /* O - Number of bytes formatted */
52_mxml_snprintf(char *buffer, /* I - Output buffer */
53 size_t bufsize, /* I - Size of output buffer */
54 const char *format, /* I - Printf-style format string */
55 ...) /* I - Additional arguments as needed */
56{
57 va_list ap; /* Argument list */
58 int bytes; /* Number of bytes formatted */
59
60
61 va_start(ap, format);
62 bytes = vsnprintf(buffer, bufsize, format, ap);
63 va_end(ap);
64
65 return (bytes);
66}
67#endif /* !HAVE_SNPRINTF */
68
69
70/*
71 * '_mxml_strdup()' - Duplicate a string.
72 */
73
74#ifndef HAVE_STRDUP
75char * /* O - New string pointer */
76_mxml_strdup(const char *s) /* I - String to duplicate */
77{
78 char *t; /* New string pointer */
79
80
81 if (s == NULL)
82 return (NULL);
83
84 if ((t = malloc(strlen(s) + 1)) == NULL)
85 return (NULL);
86
87 return (strcpy(t, s));
88}
89#endif /* !HAVE_STRDUP */
90
91
92/*
93 * '_mxml_strdupf()' - Format and duplicate a string.
94 */
95
96char * /* O - New string pointer */
97_mxml_strdupf(const char *format, /* I - Printf-style format string */
98 ...) /* I - Additional arguments as needed */
99{
100 va_list ap; /* Pointer to additional arguments */
101 char *s; /* Pointer to formatted string */
102
103
104 /*
105 * Get a pointer to the additional arguments, format the string,
106 * and return it...
107 */
108
109 va_start(ap, format);
110 s = _mxml_vstrdupf(format, ap);
111 va_end(ap);
112
113 return (s);
114}
115
116
117#ifndef HAVE_VSNPRINTF
118/*
119 * '_mxml_vsnprintf()' - Format a string into a fixed size buffer.
120 */
121
122int /* O - Number of bytes formatted */
123_mxml_vsnprintf(char *buffer, /* O - Output buffer */
124 size_t bufsize, /* O - Size of output buffer */
125 const char *format, /* I - Printf-style format string */
126 va_list ap) /* I - Pointer to additional arguments */
127{
128 char *bufptr, /* Pointer to position in buffer */
129 *bufend, /* Pointer to end of buffer */
130 sign, /* Sign of format width */
131 size, /* Size character (h, l, L) */
132 type; /* Format type character */
133 int width, /* Width of field */
134 prec; /* Number of characters of precision */
135 char tformat[100], /* Temporary format string for sprintf() */
136 *tptr, /* Pointer into temporary format */
137 temp[1024]; /* Buffer for formatted numbers */
138 char *s; /* Pointer to string */
139 int slen; /* Length of string */
140 int bytes; /* Total number of bytes needed */
141
142
143 /*
144 * Loop through the format string, formatting as needed...
145 */
146
147 bufptr = buffer;
148 bufend = buffer + bufsize - 1;
149 bytes = 0;
150
151 while (*format)
152 {
153 if (*format == '%')
154 {
155 tptr = tformat;
156 *tptr++ = *format++;
157
158 if (*format == '%')
159 {
160 if (bufptr && bufptr < bufend) *bufptr++ = *format;
161 bytes ++;
162 format ++;
163 continue;
164 }
165 else if (strchr(" -+#\'", *format))
166 {
167 *tptr++ = *format;
168 sign = *format++;
169 }
170 else
171 sign = 0;
172
173 if (*format == '*')
174 {
175 /*
176 * Get width from argument...
177 */
178
179 format ++;
180 width = va_arg(ap, int);
181
182 snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width);
183 tptr += strlen(tptr);
184 }
185 else
186 {
187 width = 0;
188
189 while (isdigit(*format & 255))
190 {
191 if (tptr < (tformat + sizeof(tformat) - 1))
192 *tptr++ = *format;
193
194 width = width * 10 + *format++ - '0';
195 }
196 }
197
198 if (*format == '.')
199 {
200 if (tptr < (tformat + sizeof(tformat) - 1))
201 *tptr++ = *format;
202
203 format ++;
204
205 if (*format == '*')
206 {
207 /*
208 * Get precision from argument...
209 */
210
211 format ++;
212 prec = va_arg(ap, int);
213
214 snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec);
215 tptr += strlen(tptr);
216 }
217 else
218 {
219 prec = 0;
220
221 while (isdigit(*format & 255))
222 {
223 if (tptr < (tformat + sizeof(tformat) - 1))
224 *tptr++ = *format;
225
226 prec = prec * 10 + *format++ - '0';
227 }
228 }
229 }
230 else
231 prec = -1;
232
233 if (*format == 'l' && format[1] == 'l')
234 {
235 size = 'L';
236
237 if (tptr < (tformat + sizeof(tformat) - 2))
238 {
239 *tptr++ = 'l';
240 *tptr++ = 'l';
241 }
242
243 format += 2;
244 }
245 else if (*format == 'h' || *format == 'l' || *format == 'L')
246 {
247 if (tptr < (tformat + sizeof(tformat) - 1))
248 *tptr++ = *format;
249
250 size = *format++;
251 }
252
253 if (!*format)
254 break;
255
256 if (tptr < (tformat + sizeof(tformat) - 1))
257 *tptr++ = *format;
258
259 type = *format++;
260 *tptr = '\0';
261
262 switch (type)
263 {
264 case 'E' : /* Floating point formats */
265 case 'G' :
266 case 'e' :
267 case 'f' :
268 case 'g' :
269 if ((width + 2) > sizeof(temp))
270 break;
271
272 sprintf(temp, tformat, va_arg(ap, double));
273
274 bytes += strlen(temp);
275
276 if (bufptr)
277 {
278 if ((bufptr + strlen(temp)) > bufend)
279 {
280 strncpy(bufptr, temp, (size_t)(bufend - bufptr));
281 bufptr = bufend;
282 }
283 else
284 {
285 strcpy(bufptr, temp);
286 bufptr += strlen(temp);
287 }
288 }
289 break;
290
291 case 'B' : /* Integer formats */
292 case 'X' :
293 case 'b' :
294 case 'd' :
295 case 'i' :
296 case 'o' :
297 case 'u' :
298 case 'x' :
299 if ((width + 2) > sizeof(temp))
300 break;
301
302#ifdef HAVE_LONG_LONG
303 if (size == 'L')
304 sprintf(temp, tformat, va_arg(ap, long long));
305 else
306#endif /* HAVE_LONG_LONG */
307 sprintf(temp, tformat, va_arg(ap, int));
308
309 bytes += strlen(temp);
310
311 if (bufptr)
312 {
313 if ((bufptr + strlen(temp)) > bufend)
314 {
315 strncpy(bufptr, temp, (size_t)(bufend - bufptr));
316 bufptr = bufend;
317 }
318 else
319 {
320 strcpy(bufptr, temp);
321 bufptr += strlen(temp);
322 }
323 }
324 break;
325
326 case 'p' : /* Pointer value */
327 if ((width + 2) > sizeof(temp))
328 break;
329
330 sprintf(temp, tformat, va_arg(ap, void *));
331
332 bytes += strlen(temp);
333
334 if (bufptr)
335 {
336 if ((bufptr + strlen(temp)) > bufend)
337 {
338 strncpy(bufptr, temp, (size_t)(bufend - bufptr));
339 bufptr = bufend;
340 }
341 else
342 {
343 strcpy(bufptr, temp);
344 bufptr += strlen(temp);
345 }
346 }
347 break;
348
349 case 'c' : /* Character or character array */
350 bytes += width;
351
352 if (bufptr)
353 {
354 if (width <= 1)
355 *bufptr++ = va_arg(ap, int);
356 else
357 {
358 if ((bufptr + width) > bufend)
359 width = bufend - bufptr;
360
361 memcpy(bufptr, va_arg(ap, char *), (size_t)width);
362 bufptr += width;
363 }
364 }
365 break;
366
367 case 's' : /* String */
368 if ((s = va_arg(ap, char *)) == NULL)
369 s = "(null)";
370
371 slen = strlen(s);
372 if (slen > width && prec != width)
373 width = slen;
374
375 bytes += width;
376
377 if (bufptr)
378 {
379 if ((bufptr + width) > bufend)
380 width = bufend - bufptr;
381
382 if (slen > width)
383 slen = width;
384
385 if (sign == '-')
386 {
387 strncpy(bufptr, s, (size_t)slen);
388 memset(bufptr + slen, ' ', (size_t)(width - slen));
389 }
390 else
391 {
392 memset(bufptr, ' ', (size_t)(width - slen));
393 strncpy(bufptr + width - slen, s, (size_t)slen);
394 }
395
396 bufptr += width;
397 }
398 break;
399
400 case 'n' : /* Output number of chars so far */
401 *(va_arg(ap, int *)) = bytes;
402 break;
403 }
404 }
405 else
406 {
407 bytes ++;
408
409 if (bufptr && bufptr < bufend)
410 *bufptr++ = *format;
411
412 format ++;
413 }
414 }
415
416 /*
417 * Nul-terminate the string and return the number of characters needed.
418 */
419
420 *bufptr = '\0';
421
422 return (bytes);
423}
424#endif /* !HAVE_VSNPRINTF */
425
426
427/*
428 * '_mxml_vstrdupf()' - Format and duplicate a string.
429 */
430
431char * /* O - New string pointer */
432_mxml_vstrdupf(const char *format, /* I - Printf-style format string */
433 va_list ap) /* I - Pointer to additional arguments */
434{
435 int bytes; /* Number of bytes required */
436 char *buffer, /* String buffer */
437 temp[256]; /* Small buffer for first vsnprintf */
438 va_list apcopy; /* Copy of argument list */
439
440
441 /*
442 * First format with a tiny buffer; this will tell us how many bytes are
443 * needed...
444 */
445
446 va_copy(apcopy, ap);
447 bytes = vsnprintf(temp, sizeof(temp), format, apcopy);
448
449 if (bytes < sizeof(temp))
450 {
451 /*
452 * Hey, the formatted string fits in the tiny buffer, so just dup that...
453 */
454
455 return (strdup(temp));
456 }
457
458 /*
459 * Allocate memory for the whole thing and reformat to the new, larger
460 * buffer...
461 */
462
463 if ((buffer = calloc(1, bytes + 1)) != NULL)
464 vsnprintf(buffer, bytes + 1, format, ap);
465
466 /*
467 * Return the new string...
468 */
469
470 return (buffer);
471}
472
473
474/*
475 * End of "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $".
476 */
diff --git a/pathologist/src/minixml/mxmldoc.c b/pathologist/src/minixml/mxmldoc.c
new file mode 100644
index 0000000..28316ee
--- /dev/null
+++ b/pathologist/src/minixml/mxmldoc.c
@@ -0,0 +1,5809 @@
1/*#define DEBUG 1*/
2/*
3 * "$Id: mxmldoc.c 440 2011-08-11 18:51:26Z mike $"
4 *
5 * Documentation generator using Mini-XML, a small XML-like file parsing
6 * library.
7 *
8 * Copyright 2003-2011 by Michael R Sweet.
9 *
10 * These coded instructions, statements, and computer programs are the
11 * property of Michael R Sweet and are protected by Federal copyright
12 * law. Distribution and use rights are outlined in the file "COPYING"
13 * which should have been included with this file. If this file is
14 * missing or damaged, see the license at:
15 *
16 * http://www.minixml.org/
17 *
18 * Contents:
19 *
20 * main() - Main entry for test program.
21 * add_variable() - Add a variable or argument.
22 * find_public() - Find a public function, type, etc.
23 * get_comment_info() - Get info from comment.
24 * get_text() - Get the text for a node.
25 * load_cb() - Set the type of child nodes.
26 * new_documentation() - Create a new documentation tree.
27 * remove_directory() - Remove a directory.
28 * safe_strcpy() - Copy a string allowing for overlapping strings.
29 * scan_file() - Scan a source file.
30 * sort_node() - Insert a node sorted into a tree.
31 * update_comment() - Update a comment node.
32 * usage() - Show program usage...
33 * write_description() - Write the description text.
34 * write_element() - Write an element's text nodes.
35 * write_file() - Copy a file to the output.
36 * write_function() - Write documentation for a function.
37 * write_html() - Write HTML documentation.
38 * write_html_head() - Write the standard HTML header.
39 * write_man() - Write manpage documentation.
40 * write_scu() - Write a structure, class, or union.
41 * write_string() - Write a string, quoting HTML special chars as needed.
42 * write_toc() - Write a table-of-contents.
43 * write_tokens() - Write <Token> nodes for all APIs.
44 * ws_cb() - Whitespace callback for saving.
45 */
46
47/*
48 * Include necessary headers...
49 */
50
51#include "config.h"
52#include "mxml.h"
53#include <time.h>
54#include <sys/stat.h>
55#ifndef WIN32
56# include <dirent.h>
57# include <unistd.h>
58#endif /* !WIN32 */
59#ifdef __APPLE__
60# include <spawn.h>
61# include <sys/wait.h>
62extern char **environ;
63#endif /* __APPLE__ */
64
65
66/*
67 * This program scans source and header files and produces public API
68 * documentation for code that conforms to the CUPS Configuration
69 * Management Plan (CMP) coding standards. Please see the following web
70 * page for details:
71 *
72 * http://www.cups.org/cmp.html
73 *
74 * Using Mini-XML, this program creates and maintains an XML representation
75 * of the public API code documentation which can then be converted to HTML
76 * as desired. The following is a poor-man's schema:
77 *
78 * <?xml version="1.0"?>
79 * <mxmldoc xmlns="http://www.easysw.com"
80 * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
81 * xsi:schemaLocation="http://www.minixml.org/mxmldoc.xsd">
82 *
83 * <namespace name=""> [optional...]
84 * <constant name="">
85 * <description>descriptive text</description>
86 * </constant>
87 *
88 * <enumeration name="">
89 * <description>descriptive text</description>
90 * <constant name="">...</constant>
91 * </enumeration>
92 *
93 * <typedef name="">
94 * <description>descriptive text</description>
95 * <type>type string</type>
96 * </typedef>
97 *
98 * <function name="" scope="">
99 * <description>descriptive text</description>
100 * <argument name="" direction="I|O|IO" default="">
101 * <description>descriptive text</description>
102 * <type>type string</type>
103 * </argument>
104 * <returnvalue>
105 * <description>descriptive text</description>
106 * <type>type string</type>
107 * </returnvalue>
108 * <seealso>function names separated by spaces</seealso>
109 * </function>
110 *
111 * <variable name="" scope="">
112 * <description>descriptive text</description>
113 * <type>type string</type>
114 * </variable>
115 *
116 * <struct name="">
117 * <description>descriptive text</description>
118 * <variable name="">...</variable>
119 * <function name="">...</function>
120 * </struct>
121 *
122 * <union name="">
123 * <description>descriptive text</description>
124 * <variable name="">...</variable>
125 * </union>
126 *
127 * <class name="" parent="">
128 * <description>descriptive text</description>
129 * <class name="">...</class>
130 * <enumeration name="">...</enumeration>
131 * <function name="">...</function>
132 * <struct name="">...</struct>
133 * <variable name="">...</variable>
134 * </class>
135 * </namespace>
136 * </mxmldoc>
137 */
138
139
140/*
141 * Basic states for file parser...
142 */
143
144#define STATE_NONE 0 /* No state - whitespace, etc. */
145#define STATE_PREPROCESSOR 1 /* Preprocessor directive */
146#define STATE_C_COMMENT 2 /* Inside a C comment */
147#define STATE_CXX_COMMENT 3 /* Inside a C++ comment */
148#define STATE_STRING 4 /* Inside a string constant */
149#define STATE_CHARACTER 5 /* Inside a character constant */
150#define STATE_IDENTIFIER 6 /* Inside a keyword/identifier */
151
152
153/*
154 * Output modes...
155 */
156
157#define OUTPUT_NONE 0 /* No output */
158#define OUTPUT_HTML 1 /* Output HTML */
159#define OUTPUT_XML 2 /* Output XML */
160#define OUTPUT_MAN 3 /* Output nroff/man */
161#define OUTPUT_TOKENS 4 /* Output docset Tokens.xml file */
162
163
164/*
165 * Local functions...
166 */
167
168static mxml_node_t *add_variable(mxml_node_t *parent, const char *name,
169 mxml_node_t *type);
170static mxml_node_t *find_public(mxml_node_t *node, mxml_node_t *top,
171 const char *name);
172static char *get_comment_info(mxml_node_t *description);
173static char *get_text(mxml_node_t *node, char *buffer, int buflen);
174static mxml_type_t load_cb(mxml_node_t *node);
175static mxml_node_t *new_documentation(mxml_node_t **mxmldoc);
176static int remove_directory(const char *path);
177static void safe_strcpy(char *dst, const char *src);
178static int scan_file(const char *filename, FILE *fp,
179 mxml_node_t *doc);
180static void sort_node(mxml_node_t *tree, mxml_node_t *func);
181static void update_comment(mxml_node_t *parent,
182 mxml_node_t *comment);
183static void usage(const char *option);
184static void write_description(FILE *out, mxml_node_t *description,
185 const char *element, int summary);
186static void write_element(FILE *out, mxml_node_t *doc,
187 mxml_node_t *element, int mode);
188static void write_file(FILE *out, const char *file);
189static void write_function(FILE *out, mxml_node_t *doc,
190 mxml_node_t *function, int level);
191static void write_html(const char *section, const char *title,
192 const char *footerfile,
193 const char *headerfile,
194 const char *introfile, const char *cssfile,
195 const char *framefile,
196 const char *docset, const char *docversion,
197 const char *feedname, const char *feedurl,
198 mxml_node_t *doc);
199static void write_html_head(FILE *out, const char *section,
200 const char *title, const char *cssfile);
201static void write_man(const char *man_name, const char *section,
202 const char *title, const char *headerfile,
203 const char *footerfile, const char *introfile,
204 mxml_node_t *doc);
205static void write_scu(FILE *out, mxml_node_t *doc,
206 mxml_node_t *scut);
207static void write_string(FILE *out, const char *s, int mode);
208static void write_toc(FILE *out, mxml_node_t *doc,
209 const char *introfile, const char *target,
210 int xml);
211static void write_tokens(FILE *out, mxml_node_t *doc,
212 const char *path);
213static const char *ws_cb(mxml_node_t *node, int where);
214
215
216/*
217 * 'main()' - Main entry for test program.
218 */
219
220int /* O - Exit status */
221main(int argc, /* I - Number of command-line args */
222 char *argv[]) /* I - Command-line args */
223{
224 int i; /* Looping var */
225 int len; /* Length of argument */
226 FILE *fp; /* File to read */
227 mxml_node_t *doc; /* XML documentation tree */
228 mxml_node_t *mxmldoc; /* mxmldoc node */
229 const char *cssfile, /* CSS stylesheet file */
230 *docset, /* Documentation set directory */
231 *docversion, /* Documentation set version */
232 *feedname, /* Feed name for documentation set */
233 *feedurl, /* Feed URL for documentation set */
234 *footerfile, /* Footer file */
235 *framefile, /* Framed HTML basename */
236 *headerfile, /* Header file */
237 *introfile, /* Introduction file */
238 *name, /* Name of manpage */
239 *path, /* Path to help file for tokens */
240 *section, /* Section/keywords of documentation */
241 *title, /* Title of documentation */
242 *xmlfile; /* XML file */
243 int mode, /* Output mode */
244 update; /* Updated XML file */
245
246
247 /*
248 * Check arguments...
249 */
250
251 cssfile = NULL;
252 doc = NULL;
253 docset = NULL;
254 docversion = NULL;
255 feedname = NULL;
256 feedurl = NULL;
257 footerfile = NULL;
258 framefile = NULL;
259 headerfile = NULL;
260 introfile = NULL;
261 mode = OUTPUT_HTML;
262 mxmldoc = NULL;
263 name = NULL;
264 path = NULL;
265 section = NULL;
266 title = NULL;
267 update = 0;
268 xmlfile = NULL;
269
270 for (i = 1; i < argc; i ++)
271 if (!strcmp(argv[i], "--help"))
272 {
273 /*
274 * Show help...
275 */
276
277 usage(NULL);
278 }
279 else if (!strcmp(argv[i], "--version"))
280 {
281 /*
282 * Show version...
283 */
284
285 puts(MXML_VERSION + 10);
286 return (0);
287 }
288 else if (!strcmp(argv[i], "--css") && !cssfile)
289 {
290 /*
291 * Set CSS stylesheet file...
292 */
293
294 i ++;
295 if (i < argc)
296 cssfile = argv[i];
297 else
298 usage(NULL);
299 }
300 else if (!strcmp(argv[i], "--docset") && !docset)
301 {
302 /*
303 * Set documentation set directory...
304 */
305
306 i ++;
307 if (i < argc)
308 docset = argv[i];
309 else
310 usage(NULL);
311 }
312 else if (!strcmp(argv[i], "--docversion") && !docversion)
313 {
314 /*
315 * Set documentation set directory...
316 */
317
318 i ++;
319 if (i < argc)
320 docversion = argv[i];
321 else
322 usage(NULL);
323 }
324 else if (!strcmp(argv[i], "--footer") && !footerfile)
325 {
326 /*
327 * Set footer file...
328 */
329
330 i ++;
331 if (i < argc)
332 footerfile = argv[i];
333 else
334 usage(NULL);
335 }
336 else if (!strcmp(argv[i], "--feedname") && !feedname)
337 {
338 /*
339 * Set documentation set feed name...
340 */
341
342 i ++;
343 if (i < argc)
344 feedname = argv[i];
345 else
346 usage(NULL);
347 }
348 else if (!strcmp(argv[i], "--feedurl") && !feedurl)
349 {
350 /*
351 * Set documentation set feed name...
352 */
353
354 i ++;
355 if (i < argc)
356 feedurl = argv[i];
357 else
358 usage(NULL);
359 }
360 else if (!strcmp(argv[i], "--framed") && !framefile)
361 {
362 /*
363 * Set base filename for framed HTML output...
364 */
365
366 i ++;
367 if (i < argc)
368 framefile = argv[i];
369 else
370 usage(NULL);
371 }
372 else if (!strcmp(argv[i], "--header") && !headerfile)
373 {
374 /*
375 * Set header file...
376 */
377
378 i ++;
379 if (i < argc)
380 headerfile = argv[i];
381 else
382 usage(NULL);
383 }
384 else if (!strcmp(argv[i], "--intro") && !introfile)
385 {
386 /*
387 * Set intro file...
388 */
389
390 i ++;
391 if (i < argc)
392 introfile = argv[i];
393 else
394 usage(NULL);
395 }
396 else if (!strcmp(argv[i], "--man") && !name)
397 {
398 /*
399 * Output manpage...
400 */
401
402 i ++;
403 if (i < argc)
404 {
405 mode = OUTPUT_MAN;
406 name = argv[i];
407 }
408 else
409 usage(NULL);
410 }
411 else if (!strcmp(argv[i], "--no-output"))
412 mode = OUTPUT_NONE;
413 else if (!strcmp(argv[i], "--section") && !section)
414 {
415 /*
416 * Set section/keywords...
417 */
418
419 i ++;
420 if (i < argc)
421 section = argv[i];
422 else
423 usage(NULL);
424 }
425 else if (!strcmp(argv[i], "--title") && !title)
426 {
427 /*
428 * Set title...
429 */
430
431 i ++;
432 if (i < argc)
433 title = argv[i];
434 else
435 usage(NULL);
436 }
437 else if (!strcmp(argv[i], "--tokens"))
438 {
439 /*
440 * Output Tokens.xml file...
441 */
442
443 mode = OUTPUT_TOKENS;
444
445 i ++;
446 if (i < argc)
447 path = argv[i];
448 else
449 usage(NULL);
450 }
451 else if (argv[i][0] == '-')
452 {
453 /*
454 * Unknown/bad option...
455 */
456
457 usage(argv[i]);
458 }
459 else
460 {
461 /*
462 * Process XML or source file...
463 */
464
465 len = (int)strlen(argv[i]);
466 if (len > 4 && !strcmp(argv[i] + len - 4, ".xml"))
467 {
468 /*
469 * Set XML file...
470 */
471
472 if (xmlfile)
473 usage(NULL);
474
475 xmlfile = argv[i];
476
477 if (!doc)
478 {
479 if ((fp = fopen(argv[i], "r")) != NULL)
480 {
481 /*
482 * Read the existing XML file...
483 */
484
485 doc = mxmlLoadFile(NULL, fp, load_cb);
486
487 fclose(fp);
488
489 if (!doc)
490 {
491 mxmldoc = NULL;
492
493 fprintf(stderr,
494 "mxmldoc: Unable to read the XML documentation file "
495 "\"%s\"!\n", argv[i]);
496 }
497 else if ((mxmldoc = mxmlFindElement(doc, doc, "mxmldoc", NULL,
498 NULL, MXML_DESCEND)) == NULL)
499 {
500 fprintf(stderr,
501 "mxmldoc: XML documentation file \"%s\" is missing "
502 "<mxmldoc> node!!\n", argv[i]);
503
504 mxmlDelete(doc);
505 doc = NULL;
506 }
507 }
508 else
509 {
510 doc = NULL;
511 mxmldoc = NULL;
512 }
513
514 if (!doc)
515 doc = new_documentation(&mxmldoc);
516 }
517 }
518 else
519 {
520 /*
521 * Load source file...
522 */
523
524 update = 1;
525
526 if (!doc)
527 doc = new_documentation(&mxmldoc);
528
529 if ((fp = fopen(argv[i], "r")) == NULL)
530 {
531 fprintf(stderr, "mxmldoc: Unable to open source file \"%s\": %s\n",
532 argv[i], strerror(errno));
533 mxmlDelete(doc);
534 return (1);
535 }
536 else if (scan_file(argv[i], fp, mxmldoc))
537 {
538 fclose(fp);
539 mxmlDelete(doc);
540 return (1);
541 }
542 else
543 fclose(fp);
544 }
545 }
546
547 if (update && xmlfile)
548 {
549 /*
550 * Save the updated XML documentation file...
551 */
552
553 if ((fp = fopen(xmlfile, "w")) != NULL)
554 {
555 /*
556 * Write over the existing XML file...
557 */
558
559 mxmlSetWrapMargin(0);
560
561 if (mxmlSaveFile(doc, fp, ws_cb))
562 {
563 fprintf(stderr,
564 "mxmldoc: Unable to write the XML documentation file \"%s\": "
565 "%s!\n", xmlfile, strerror(errno));
566 fclose(fp);
567 mxmlDelete(doc);
568 return (1);
569 }
570
571 fclose(fp);
572 }
573 else
574 {
575 fprintf(stderr,
576 "mxmldoc: Unable to create the XML documentation file \"%s\": "
577 "%s!\n", xmlfile, strerror(errno));
578 mxmlDelete(doc);
579 return (1);
580 }
581 }
582
583 switch (mode)
584 {
585 case OUTPUT_HTML :
586 /*
587 * Write HTML documentation...
588 */
589
590 write_html(section, title ? title : "Documentation", footerfile,
591 headerfile, introfile, cssfile, framefile, docset,
592 docversion, feedname, feedurl, mxmldoc);
593 break;
594
595 case OUTPUT_MAN :
596 /*
597 * Write manpage documentation...
598 */
599
600 write_man(name, section, title, footerfile, headerfile, introfile,
601 mxmldoc);
602 break;
603
604 case OUTPUT_TOKENS :
605 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
606 "<Tokens version=\"1.0\">\n", stdout);
607
608 write_tokens(stdout, mxmldoc, path);
609
610 fputs("</Tokens>\n", stdout);
611 break;
612 }
613
614 /*
615 * Delete the tree and return...
616 */
617
618 mxmlDelete(doc);
619
620 return (0);
621}
622
623
624/*
625 * 'add_variable()' - Add a variable or argument.
626 */
627
628static mxml_node_t * /* O - New variable/argument */
629add_variable(mxml_node_t *parent, /* I - Parent node */
630 const char *name, /* I - "argument" or "variable" */
631 mxml_node_t *type) /* I - Type nodes */
632{
633 mxml_node_t *variable, /* New variable */
634 *node, /* Current node */
635 *next; /* Next node */
636 char buffer[16384], /* String buffer */
637 *bufptr; /* Pointer into buffer */
638
639
640#ifdef DEBUG
641 fprintf(stderr, "add_variable(parent=%p, name=\"%s\", type=%p)\n",
642 parent, name, type);
643#endif /* DEBUG */
644
645 /*
646 * Range check input...
647 */
648
649 if (!type || !type->child)
650 return (NULL);
651
652 /*
653 * Create the variable/argument node...
654 */
655
656 variable = mxmlNewElement(parent, name);
657
658 /*
659 * Check for a default value...
660 */
661
662 for (node = type->child; node; node = node->next)
663 if (!strcmp(node->value.text.string, "="))
664 break;
665
666 if (node)
667 {
668 /*
669 * Default value found, copy it and add as a "default" attribute...
670 */
671
672 for (bufptr = buffer; node; bufptr += strlen(bufptr))
673 {
674 if (node->value.text.whitespace && bufptr > buffer)
675 *bufptr++ = ' ';
676
677 strcpy(bufptr, node->value.text.string);
678
679 next = node->next;
680 mxmlDelete(node);
681 node = next;
682 }
683
684 mxmlElementSetAttr(variable, "default", buffer);
685 }
686
687 /*
688 * Extract the argument/variable name...
689 */
690
691 if (type->last_child->value.text.string[0] == ')')
692 {
693 /*
694 * Handle "type (*name)(args)"...
695 */
696
697 for (node = type->child; node; node = node->next)
698 if (node->value.text.string[0] == '(')
699 break;
700
701 for (bufptr = buffer; node; bufptr += strlen(bufptr))
702 {
703 if (node->value.text.whitespace && bufptr > buffer)
704 *bufptr++ = ' ';
705
706 strcpy(bufptr, node->value.text.string);
707
708 next = node->next;
709 mxmlDelete(node);
710 node = next;
711 }
712 }
713 else
714 {
715 /*
716 * Handle "type name"...
717 */
718
719 strcpy(buffer, type->last_child->value.text.string);
720 mxmlDelete(type->last_child);
721 }
722
723 /*
724 * Set the name...
725 */
726
727 mxmlElementSetAttr(variable, "name", buffer);
728
729 /*
730 * Add the remaining type information to the variable node...
731 */
732
733 mxmlAdd(variable, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, type);
734
735 /*
736 * Add new new variable node...
737 */
738
739 return (variable);
740}
741
742
743/*
744 * 'find_public()' - Find a public function, type, etc.
745 */
746
747static mxml_node_t * /* I - Found node or NULL */
748find_public(mxml_node_t *node, /* I - Current node */
749 mxml_node_t *top, /* I - Top node */
750 const char *name) /* I - Name of element */
751{
752 mxml_node_t *description, /* Description node */
753 *comment; /* Comment node */
754
755
756 for (node = mxmlFindElement(node, top, name, NULL, NULL,
757 node == top ? MXML_DESCEND_FIRST :
758 MXML_NO_DESCEND);
759 node;
760 node = mxmlFindElement(node, top, name, NULL, NULL, MXML_NO_DESCEND))
761 {
762 /*
763 * Get the description for this node...
764 */
765
766 description = mxmlFindElement(node, node, "description", NULL, NULL,
767 MXML_DESCEND_FIRST);
768
769 /*
770 * A missing or empty description signals a private node...
771 */
772
773 if (!description)
774 continue;
775
776 /*
777 * Look for @private@ in the comment text...
778 */
779
780 for (comment = description->child; comment; comment = comment->next)
781 if ((comment->type == MXML_TEXT &&
782 strstr(comment->value.text.string, "@private@")) ||
783 (comment->type == MXML_OPAQUE &&
784 strstr(comment->value.opaque, "@private@")))
785 break;
786
787 if (!comment)
788 {
789 /*
790 * No @private@, so return this node...
791 */
792
793 return (node);
794 }
795 }
796
797 /*
798 * If we get here, there are no (more) public nodes...
799 */
800
801 return (NULL);
802}
803
804
805/*
806 * 'get_comment_info()' - Get info from comment.
807 */
808
809static char * /* O - Info from comment */
810get_comment_info(
811 mxml_node_t *description) /* I - Description node */
812{
813 char text[10240], /* Description text */
814 since[255], /* @since value */
815 *ptr; /* Pointer into text */
816 static char info[1024]; /* Info string */
817
818
819 if (!description)
820 return ("");
821
822 get_text(description, text, sizeof(text));
823
824 for (ptr = strchr(text, '@'); ptr; ptr = strchr(ptr + 1, '@'))
825 {
826 if (!strncmp(ptr, "@deprecated@", 12))
827 return ("<span class=\"info\">&nbsp;DEPRECATED&nbsp;</span>");
828 else if (!strncmp(ptr, "@since ", 7))
829 {
830 strncpy(since, ptr + 7, sizeof(since) - 1);
831 since[sizeof(since) - 1] = '\0';
832
833 if ((ptr = strchr(since, '@')) != NULL)
834 *ptr = '\0';
835
836 snprintf(info, sizeof(info), "<span class=\"info\">&nbsp;%s&nbsp;</span>", since);
837 return (info);
838 }
839 }
840
841 return ("");
842}
843
844
845/*
846 * 'get_text()' - Get the text for a node.
847 */
848
849static char * /* O - Text in node */
850get_text(mxml_node_t *node, /* I - Node to get */
851 char *buffer, /* I - Buffer */
852 int buflen) /* I - Size of buffer */
853{
854 char *ptr, /* Pointer into buffer */
855 *end; /* End of buffer */
856 int len; /* Length of node */
857 mxml_node_t *current; /* Current node */
858
859
860 ptr = buffer;
861 end = buffer + buflen - 1;
862
863 for (current = node->child; current && ptr < end; current = current->next)
864 {
865 if (current->type == MXML_TEXT)
866 {
867 if (current->value.text.whitespace)
868 *ptr++ = ' ';
869
870 len = (int)strlen(current->value.text.string);
871 if (len > (int)(end - ptr))
872 len = (int)(end - ptr);
873
874 memcpy(ptr, current->value.text.string, len);
875 ptr += len;
876 }
877 else if (current->type == MXML_OPAQUE)
878 {
879 len = (int)strlen(current->value.opaque);
880 if (len > (int)(end - ptr))
881 len = (int)(end - ptr);
882
883 memcpy(ptr, current->value.opaque, len);
884 ptr += len;
885 }
886 }
887
888 *ptr = '\0';
889
890 return (buffer);
891}
892
893
894/*
895 * 'load_cb()' - Set the type of child nodes.
896 */
897
898static mxml_type_t /* O - Node type */
899load_cb(mxml_node_t *node) /* I - Node */
900{
901 if (!strcmp(node->value.element.name, "description"))
902 return (MXML_OPAQUE);
903 else
904 return (MXML_TEXT);
905}
906
907
908/*
909 * 'new_documentation()' - Create a new documentation tree.
910 */
911
912static mxml_node_t * /* O - New documentation */
913new_documentation(mxml_node_t **mxmldoc)/* O - mxmldoc node */
914{
915 mxml_node_t *doc; /* New documentation */
916
917
918 /*
919 * Create an empty XML documentation file...
920 */
921
922 doc = mxmlNewXML(NULL);
923
924 *mxmldoc = mxmlNewElement(doc, "mxmldoc");
925
926 mxmlElementSetAttr(*mxmldoc, "xmlns", "http://www.easysw.com");
927 mxmlElementSetAttr(*mxmldoc, "xmlns:xsi",
928 "http://www.w3.org/2001/XMLSchema-instance");
929 mxmlElementSetAttr(*mxmldoc, "xsi:schemaLocation",
930 "http://www.minixml.org/mxmldoc.xsd");
931
932 return (doc);
933}
934
935
936/*
937 * 'remove_directory()' - Remove a directory.
938 */
939
940static int /* O - 1 on success, 0 on failure */
941remove_directory(const char *path) /* I - Directory to remove */
942{
943#ifdef WIN32
944 /* TODO: Add Windows directory removal code */
945
946#else
947 DIR *dir; /* Directory */
948 struct dirent *dent; /* Current directory entry */
949 char filename[1024]; /* Current filename */
950 struct stat fileinfo; /* File information */
951
952
953 if ((dir = opendir(path)) == NULL)
954 {
955 fprintf(stderr, "mxmldoc: Unable to open directory \"%s\": %s\n", path,
956 strerror(errno));
957 return (0);
958 }
959
960 while ((dent = readdir(dir)) != NULL)
961 {
962 /*
963 * Skip "." and ".."...
964 */
965
966 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
967 continue;
968
969 /*
970 * See if we have a file or directory...
971 */
972
973 snprintf(filename, sizeof(filename), "%s/%s", path, dent->d_name);
974
975 if (stat(filename, &fileinfo))
976 {
977 fprintf(stderr, "mxmldoc: Unable to stat \"%s\": %s\n", filename,
978 strerror(errno));
979 closedir(dir);
980 return (0);
981 }
982
983 if (S_ISDIR(fileinfo.st_mode))
984 {
985 if (!remove_directory(filename))
986 {
987 closedir(dir);
988 return (0);
989 }
990 }
991 else if (unlink(filename))
992 {
993 fprintf(stderr, "mxmldoc: Unable to remove \"%s\": %s\n", filename,
994 strerror(errno));
995 closedir(dir);
996 return (0);
997 }
998 }
999
1000 closedir(dir);
1001
1002 if (rmdir(path))
1003 {
1004 fprintf(stderr, "mxmldoc: Unable to remove directory \"%s\": %s\n", path,
1005 strerror(errno));
1006 return (0);
1007 }
1008#endif /* WIN32 */
1009
1010 return (1);
1011}
1012
1013
1014/*
1015 * 'safe_strcpy()' - Copy a string allowing for overlapping strings.
1016 */
1017
1018static void
1019safe_strcpy(char *dst, /* I - Destination string */
1020 const char *src) /* I - Source string */
1021{
1022 while (*src)
1023 *dst++ = *src++;
1024
1025 *dst = '\0';
1026}
1027
1028
1029/*
1030 * 'scan_file()' - Scan a source file.
1031 */
1032
1033static int /* O - 0 on success, -1 on error */
1034scan_file(const char *filename, /* I - Filename */
1035 FILE *fp, /* I - File to scan */
1036 mxml_node_t *tree) /* I - Function tree */
1037{
1038 int state, /* Current parser state */
1039 braces, /* Number of braces active */
1040 parens; /* Number of active parenthesis */
1041 int ch; /* Current character */
1042 char buffer[65536], /* String buffer */
1043 *bufptr; /* Pointer into buffer */
1044 const char *scope; /* Current variable/function scope */
1045 mxml_node_t *comment, /* <comment> node */
1046 *constant, /* <constant> node */
1047 *enumeration, /* <enumeration> node */
1048 *function, /* <function> node */
1049 *fstructclass, /* function struct/class node */
1050 *structclass, /* <struct> or <class> node */
1051 *typedefnode, /* <typedef> node */
1052 *variable, /* <variable> or <argument> node */
1053 *returnvalue, /* <returnvalue> node */
1054 *type, /* <type> node */
1055 *description, /* <description> node */
1056 *node, /* Current node */
1057 *next; /* Next node */
1058#if DEBUG > 1
1059 mxml_node_t *temp; /* Temporary node */
1060 int oldstate, /* Previous state */
1061 oldch; /* Old character */
1062 static const char *states[] = /* State strings */
1063 {
1064 "STATE_NONE",
1065 "STATE_PREPROCESSOR",
1066 "STATE_C_COMMENT",
1067 "STATE_CXX_COMMENT",
1068 "STATE_STRING",
1069 "STATE_CHARACTER",
1070 "STATE_IDENTIFIER"
1071 };
1072#endif /* DEBUG > 1 */
1073
1074
1075#ifdef DEBUG
1076 fprintf(stderr, "scan_file(filename=\"%s\", fp=%p, tree=%p)\n", filename,
1077 fp, tree);
1078#endif /* DEBUG */
1079
1080 /*
1081 * Initialize the finite state machine...
1082 */
1083
1084 state = STATE_NONE;
1085 braces = 0;
1086 parens = 0;
1087 bufptr = buffer;
1088
1089 comment = mxmlNewElement(MXML_NO_PARENT, "temp");
1090 constant = NULL;
1091 enumeration = NULL;
1092 function = NULL;
1093 variable = NULL;
1094 returnvalue = NULL;
1095 type = NULL;
1096 description = NULL;
1097 typedefnode = NULL;
1098 structclass = NULL;
1099 fstructclass = NULL;
1100
1101 if (!strcmp(tree->value.element.name, "class"))
1102 scope = "private";
1103 else
1104 scope = NULL;
1105
1106 /*
1107 * Read until end-of-file...
1108 */
1109
1110 while ((ch = getc(fp)) != EOF)
1111 {
1112#if DEBUG > 1
1113 oldstate = state;
1114 oldch = ch;
1115#endif /* DEBUG > 1 */
1116
1117 switch (state)
1118 {
1119 case STATE_NONE : /* No state - whitespace, etc. */
1120 switch (ch)
1121 {
1122 case '/' : /* Possible C/C++ comment */
1123 ch = getc(fp);
1124 bufptr = buffer;
1125
1126 if (ch == '*')
1127 state = STATE_C_COMMENT;
1128 else if (ch == '/')
1129 state = STATE_CXX_COMMENT;
1130 else
1131 {
1132 ungetc(ch, fp);
1133
1134 if (type)
1135 {
1136#ifdef DEBUG
1137 fputs("Identifier: <<<< / >>>\n", stderr);
1138#endif /* DEBUG */
1139 ch = type->last_child->value.text.string[0];
1140 mxmlNewText(type, isalnum(ch) || ch == '_', "/");
1141 }
1142 }
1143 break;
1144
1145 case '#' : /* Preprocessor */
1146#ifdef DEBUG
1147 fputs(" #preprocessor...\n", stderr);
1148#endif /* DEBUG */
1149 state = STATE_PREPROCESSOR;
1150 break;
1151
1152 case '\'' : /* Character constant */
1153 state = STATE_CHARACTER;
1154 bufptr = buffer;
1155 *bufptr++ = ch;
1156 break;
1157
1158 case '\"' : /* String constant */
1159 state = STATE_STRING;
1160 bufptr = buffer;
1161 *bufptr++ = ch;
1162 break;
1163
1164 case '{' :
1165#ifdef DEBUG
1166 fprintf(stderr, " open brace, function=%p, type=%p...\n",
1167 function, type);
1168 if (type)
1169 fprintf(stderr, " type->child=\"%s\"...\n",
1170 type->child->value.text.string);
1171#endif /* DEBUG */
1172
1173 if (function)
1174 {
1175 if (fstructclass)
1176 {
1177 sort_node(fstructclass, function);
1178 fstructclass = NULL;
1179 }
1180 else
1181 sort_node(tree, function);
1182
1183 function = NULL;
1184 }
1185 else if (type && type->child &&
1186 ((!strcmp(type->child->value.text.string, "typedef") &&
1187 type->child->next &&
1188 (!strcmp(type->child->next->value.text.string, "struct") ||
1189 !strcmp(type->child->next->value.text.string, "union") ||
1190 !strcmp(type->child->next->value.text.string, "class"))) ||
1191 !strcmp(type->child->value.text.string, "union") ||
1192 !strcmp(type->child->value.text.string, "struct") ||
1193 !strcmp(type->child->value.text.string, "class")))
1194 {
1195 /*
1196 * Start of a class or structure...
1197 */
1198
1199 if (!strcmp(type->child->value.text.string, "typedef"))
1200 {
1201#ifdef DEBUG
1202 fputs(" starting typedef...\n", stderr);
1203#endif /* DEBUG */
1204
1205 typedefnode = mxmlNewElement(MXML_NO_PARENT, "typedef");
1206 mxmlDelete(type->child);
1207 }
1208 else
1209 typedefnode = NULL;
1210
1211 structclass = mxmlNewElement(MXML_NO_PARENT,
1212 type->child->value.text.string);
1213
1214#ifdef DEBUG
1215 fprintf(stderr, "%c%s: <<<< %s >>>\n",
1216 toupper(type->child->value.text.string[0]),
1217 type->child->value.text.string + 1,
1218 type->child->next ?
1219 type->child->next->value.text.string : "(noname)");
1220
1221 fputs(" type =", stderr);
1222 for (node = type->child; node; node = node->next)
1223 fprintf(stderr, " \"%s\"", node->value.text.string);
1224 putc('\n', stderr);
1225
1226 fprintf(stderr, " scope = %s\n", scope ? scope : "(null)");
1227#endif /* DEBUG */
1228
1229 if (type->child->next)
1230 {
1231 mxmlElementSetAttr(structclass, "name",
1232 type->child->next->value.text.string);
1233 sort_node(tree, structclass);
1234 }
1235
1236 if (typedefnode && type->child)
1237 type->child->value.text.whitespace = 0;
1238 else if (structclass && type->child &&
1239 type->child->next && type->child->next->next)
1240 {
1241 for (bufptr = buffer, node = type->child->next->next;
1242 node;
1243 bufptr += strlen(bufptr))
1244 {
1245 if (node->value.text.whitespace && bufptr > buffer)
1246 *bufptr++ = ' ';
1247
1248 strcpy(bufptr, node->value.text.string);
1249
1250 next = node->next;
1251 mxmlDelete(node);
1252 node = next;
1253 }
1254
1255 mxmlElementSetAttr(structclass, "parent", buffer);
1256
1257 mxmlDelete(type);
1258 type = NULL;
1259 }
1260 else
1261 {
1262 mxmlDelete(type);
1263 type = NULL;
1264 }
1265
1266 if (typedefnode && comment->last_child)
1267 {
1268 /*
1269 * Copy comment for typedef as well as class/struct/union...
1270 */
1271
1272 mxmlNewText(comment, 0,
1273 comment->last_child->value.text.string);
1274 description = mxmlNewElement(typedefnode, "description");
1275#ifdef DEBUG
1276 fprintf(stderr,
1277 " duplicating comment %p/%p for typedef...\n",
1278 comment->last_child, comment->child);
1279#endif /* DEBUG */
1280 update_comment(typedefnode, comment->last_child);
1281 mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT,
1282 comment->last_child);
1283 }
1284
1285 description = mxmlNewElement(structclass, "description");
1286#ifdef DEBUG
1287 fprintf(stderr, " adding comment %p/%p to %s...\n",
1288 comment->last_child, comment->child,
1289 structclass->value.element.name);
1290#endif /* DEBUG */
1291 update_comment(structclass, comment->last_child);
1292 mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT,
1293 comment->last_child);
1294
1295 if (scan_file(filename, fp, structclass))
1296 {
1297 mxmlDelete(comment);
1298 return (-1);
1299 }
1300
1301#ifdef DEBUG
1302 fputs(" ended typedef...\n", stderr);
1303#endif /* DEBUG */
1304 structclass = NULL;
1305 break;
1306 }
1307 else if (type && type->child && type->child->next &&
1308 (!strcmp(type->child->value.text.string, "enum") ||
1309 (!strcmp(type->child->value.text.string, "typedef") &&
1310 !strcmp(type->child->next->value.text.string, "enum"))))
1311 {
1312 /*
1313 * Enumeration type...
1314 */
1315
1316 if (!strcmp(type->child->value.text.string, "typedef"))
1317 {
1318#ifdef DEBUG
1319 fputs(" starting typedef...\n", stderr);
1320#endif /* DEBUG */
1321
1322 typedefnode = mxmlNewElement(MXML_NO_PARENT, "typedef");
1323 mxmlDelete(type->child);
1324 }
1325 else
1326 typedefnode = NULL;
1327
1328 enumeration = mxmlNewElement(MXML_NO_PARENT, "enumeration");
1329
1330#ifdef DEBUG
1331 fprintf(stderr, "Enumeration: <<<< %s >>>\n",
1332 type->child->next ?
1333 type->child->next->value.text.string : "(noname)");
1334#endif /* DEBUG */
1335
1336 if (type->child->next)
1337 {
1338 mxmlElementSetAttr(enumeration, "name",
1339 type->child->next->value.text.string);
1340 sort_node(tree, enumeration);
1341 }
1342
1343 if (typedefnode && type->child)
1344 type->child->value.text.whitespace = 0;
1345 else
1346 {
1347 mxmlDelete(type);
1348 type = NULL;
1349 }
1350
1351 if (typedefnode && comment->last_child)
1352 {
1353 /*
1354 * Copy comment for typedef as well as class/struct/union...
1355 */
1356
1357 mxmlNewText(comment, 0,
1358 comment->last_child->value.text.string);
1359 description = mxmlNewElement(typedefnode, "description");
1360#ifdef DEBUG
1361 fprintf(stderr,
1362 " duplicating comment %p/%p for typedef...\n",
1363 comment->last_child, comment->child);
1364#endif /* DEBUG */
1365 update_comment(typedefnode, comment->last_child);
1366 mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT,
1367 comment->last_child);
1368 }
1369
1370 description = mxmlNewElement(enumeration, "description");
1371#ifdef DEBUG
1372 fprintf(stderr, " adding comment %p/%p to enumeration...\n",
1373 comment->last_child, comment->child);
1374#endif /* DEBUG */
1375 update_comment(enumeration, comment->last_child);
1376 mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT,
1377 comment->last_child);
1378 }
1379 else if (type && type->child &&
1380 !strcmp(type->child->value.text.string, "extern"))
1381 {
1382 if (scan_file(filename, fp, tree))
1383 {
1384 mxmlDelete(comment);
1385 return (-1);
1386 }
1387 }
1388 else if (type)
1389 {
1390 mxmlDelete(type);
1391 type = NULL;
1392 }
1393
1394 braces ++;
1395 function = NULL;
1396 variable = NULL;
1397 break;
1398
1399 case '}' :
1400#ifdef DEBUG
1401 fputs(" close brace...\n", stderr);
1402#endif /* DEBUG */
1403
1404 if (structclass)
1405 scope = NULL;
1406
1407 if (!typedefnode)
1408 enumeration = NULL;
1409
1410 constant = NULL;
1411 structclass = NULL;
1412
1413 if (braces > 0)
1414 braces --;
1415 else
1416 {
1417 mxmlDelete(comment);
1418 return (0);
1419 }
1420 break;
1421
1422 case '(' :
1423 if (type)
1424 {
1425#ifdef DEBUG
1426 fputs("Identifier: <<<< ( >>>\n", stderr);
1427#endif /* DEBUG */
1428 mxmlNewText(type, 0, "(");
1429 }
1430
1431 parens ++;
1432 break;
1433
1434 case ')' :
1435 if (type && parens)
1436 {
1437#ifdef DEBUG
1438 fputs("Identifier: <<<< ) >>>\n", stderr);
1439#endif /* DEBUG */
1440 mxmlNewText(type, 0, ")");
1441 }
1442
1443 if (function && type && !parens)
1444 {
1445 /*
1446 * Check for "void" argument...
1447 */
1448
1449 if (type->child && type->child->next)
1450 variable = add_variable(function, "argument", type);
1451 else
1452 mxmlDelete(type);
1453
1454 type = NULL;
1455 }
1456
1457 if (parens > 0)
1458 parens --;
1459 break;
1460
1461 case ';' :
1462#ifdef DEBUG
1463 fputs("Identifier: <<<< ; >>>\n", stderr);
1464 fprintf(stderr, " enumeration=%p, function=%p, type=%p, type->child=%p, typedefnode=%p\n",
1465 enumeration, function, type, type ? type->child : NULL, typedefnode);
1466#endif /* DEBUG */
1467
1468 if (function)
1469 {
1470 if (!strcmp(tree->value.element.name, "class"))
1471 {
1472#ifdef DEBUG
1473 fputs(" ADDING FUNCTION TO CLASS\n", stderr);
1474#endif /* DEBUG */
1475 sort_node(tree, function);
1476 }
1477 else
1478 mxmlDelete(function);
1479
1480 function = NULL;
1481 variable = NULL;
1482 }
1483
1484 if (type)
1485 {
1486 /*
1487 * See if we have a typedef...
1488 */
1489
1490 if (type->child &&
1491 !strcmp(type->child->value.text.string, "typedef"))
1492 {
1493 /*
1494 * Yes, add it!
1495 */
1496
1497 typedefnode = mxmlNewElement(MXML_NO_PARENT, "typedef");
1498
1499 for (node = type->child->next; node; node = node->next)
1500 if (!strcmp(node->value.text.string, "("))
1501 break;
1502
1503 if (node)
1504 {
1505 for (node = node->next; node; node = node->next)
1506 if (strcmp(node->value.text.string, "*"))
1507 break;
1508 }
1509
1510 if (!node)
1511 node = type->last_child;
1512
1513#ifdef DEBUG
1514 fprintf(stderr, " ADDING TYPEDEF FOR %p(%s)...\n",
1515 node, node->value.text.string);
1516#endif /* DEBUG */
1517
1518 mxmlElementSetAttr(typedefnode, "name",
1519 node->value.text.string);
1520 sort_node(tree, typedefnode);
1521
1522 if (type->child != node)
1523 mxmlDelete(type->child);
1524
1525 mxmlDelete(node);
1526
1527 if (type->child)
1528 type->child->value.text.whitespace = 0;
1529
1530 mxmlAdd(typedefnode, MXML_ADD_AFTER, MXML_ADD_TO_PARENT,
1531 type);
1532 type = NULL;
1533 break;
1534 }
1535 else if (typedefnode && enumeration)
1536 {
1537 /*
1538 * Add enum typedef...
1539 */
1540
1541 node = type->child;
1542
1543#ifdef DEBUG
1544 fprintf(stderr, " ADDING TYPEDEF FOR %p(%s)...\n",
1545 node, node->value.text.string);
1546#endif /* DEBUG */
1547
1548 mxmlElementSetAttr(typedefnode, "name",
1549 node->value.text.string);
1550 sort_node(tree, typedefnode);
1551 mxmlDelete(type);
1552
1553 type = mxmlNewElement(typedefnode, "type");
1554 mxmlNewText(type, 0, "enum");
1555 mxmlNewText(type, 1,
1556 mxmlElementGetAttr(enumeration, "name"));
1557 enumeration = NULL;
1558 type = NULL;
1559 break;
1560 }
1561
1562 mxmlDelete(type);
1563 type = NULL;
1564 }
1565 break;
1566
1567 case ':' :
1568 if (type)
1569 {
1570#ifdef DEBUG
1571 fputs("Identifier: <<<< : >>>\n", stderr);
1572#endif /* DEBUG */
1573 mxmlNewText(type, 1, ":");
1574 }
1575 break;
1576
1577 case '*' :
1578 if (type)
1579 {
1580#ifdef DEBUG
1581 fputs("Identifier: <<<< * >>>\n", stderr);
1582#endif /* DEBUG */
1583 ch = type->last_child->value.text.string[0];
1584 mxmlNewText(type, isalnum(ch) || ch == '_', "*");
1585 }
1586 break;
1587
1588 case ',' :
1589 if (type && !enumeration)
1590 {
1591#ifdef DEBUG
1592 fputs("Identifier: <<<< , >>>\n", stderr);
1593#endif /* DEBUG */
1594 mxmlNewText(type, 0, ",");
1595 }
1596 break;
1597
1598 case '&' :
1599 if (type)
1600 {
1601#ifdef DEBUG
1602 fputs("Identifier: <<<< & >>>\n", stderr);
1603#endif /* DEBUG */
1604 mxmlNewText(type, 1, "&");
1605 }
1606 break;
1607
1608 case '+' :
1609 if (type)
1610 {
1611#ifdef DEBUG
1612 fputs("Identifier: <<<< + >>>\n", stderr);
1613#endif /* DEBUG */
1614 ch = type->last_child->value.text.string[0];
1615 mxmlNewText(type, isalnum(ch) || ch == '_', "+");
1616 }
1617 break;
1618
1619 case '-' :
1620 if (type)
1621 {
1622#ifdef DEBUG
1623 fputs("Identifier: <<<< - >>>\n", stderr);
1624#endif /* DEBUG */
1625 ch = type->last_child->value.text.string[0];
1626 mxmlNewText(type, isalnum(ch) || ch == '_', "-");
1627 }
1628 break;
1629
1630 case '=' :
1631 if (type)
1632 {
1633#ifdef DEBUG
1634 fputs("Identifier: <<<< = >>>\n", stderr);
1635#endif /* DEBUG */
1636 ch = type->last_child->value.text.string[0];
1637 mxmlNewText(type, isalnum(ch) || ch == '_', "=");
1638 }
1639 break;
1640
1641 default : /* Other */
1642 if (isalnum(ch) || ch == '_' || ch == '.' || ch == ':' || ch == '~')
1643 {
1644 state = STATE_IDENTIFIER;
1645 bufptr = buffer;
1646 *bufptr++ = ch;
1647 }
1648 break;
1649 }
1650 break;
1651
1652 case STATE_PREPROCESSOR : /* Preprocessor directive */
1653 if (ch == '\n')
1654 state = STATE_NONE;
1655 else if (ch == '\\')
1656 getc(fp);
1657 break;
1658
1659 case STATE_C_COMMENT : /* Inside a C comment */
1660 switch (ch)
1661 {
1662 case '\n' :
1663 while ((ch = getc(fp)) != EOF)
1664 if (ch == '*')
1665 {
1666 ch = getc(fp);
1667
1668 if (ch == '/')
1669 {
1670 *bufptr = '\0';
1671
1672 if (comment->child != comment->last_child)
1673 {
1674#ifdef DEBUG
1675 fprintf(stderr, " removing comment %p(%20.20s), last comment %p(%20.20s)...\n",
1676 comment->child,
1677 comment->child ? comment->child->value.text.string : "",
1678 comment->last_child,
1679 comment->last_child ? comment->last_child->value.text.string : "");
1680#endif /* DEBUG */
1681 mxmlDelete(comment->child);
1682#ifdef DEBUG
1683 fprintf(stderr, " new comment %p, last comment %p...\n",
1684 comment->child, comment->last_child);
1685#endif /* DEBUG */
1686 }
1687
1688#ifdef DEBUG
1689 fprintf(stderr,
1690 " processing comment, variable=%p, "
1691 "constant=%p, typedefnode=%p, tree=\"%s\"\n",
1692 variable, constant, typedefnode,
1693 tree->value.element.name);
1694#endif /* DEBUG */
1695
1696 if (variable)
1697 {
1698 if (strstr(buffer, "@private@"))
1699 {
1700 /*
1701 * Delete private variables...
1702 */
1703
1704 mxmlDelete(variable);
1705 }
1706 else
1707 {
1708 description = mxmlNewElement(variable, "description");
1709#ifdef DEBUG
1710 fprintf(stderr,
1711 " adding comment %p/%p to variable...\n",
1712 comment->last_child, comment->child);
1713#endif /* DEBUG */
1714 mxmlNewText(comment, 0, buffer);
1715 update_comment(variable,
1716 mxmlNewText(description, 0, buffer));
1717 }
1718
1719 variable = NULL;
1720 }
1721 else if (constant)
1722 {
1723 if (strstr(buffer, "@private@"))
1724 {
1725 /*
1726 * Delete private constants...
1727 */
1728
1729 mxmlDelete(constant);
1730 }
1731 else
1732 {
1733 description = mxmlNewElement(constant, "description");
1734#ifdef DEBUG
1735 fprintf(stderr,
1736 " adding comment %p/%p to constant...\n",
1737 comment->last_child, comment->child);
1738#endif /* DEBUG */
1739 mxmlNewText(comment, 0, buffer);
1740 update_comment(constant,
1741 mxmlNewText(description, 0, buffer));
1742 }
1743
1744 constant = NULL;
1745 }
1746 else if (typedefnode)
1747 {
1748 if (strstr(buffer, "@private@"))
1749 {
1750 /*
1751 * Delete private typedefs...
1752 */
1753
1754 mxmlDelete(typedefnode);
1755
1756 if (structclass)
1757 {
1758 mxmlDelete(structclass);
1759 structclass = NULL;
1760 }
1761
1762 if (enumeration)
1763 {
1764 mxmlDelete(enumeration);
1765 enumeration = NULL;
1766 }
1767 }
1768 else
1769 {
1770 description = mxmlNewElement(typedefnode, "description");
1771#ifdef DEBUG
1772 fprintf(stderr,
1773 " adding comment %p/%p to typedef %s...\n",
1774 comment->last_child, comment->child,
1775 mxmlElementGetAttr(typedefnode, "name"));
1776#endif /* DEBUG */
1777 mxmlNewText(comment, 0, buffer);
1778 update_comment(typedefnode,
1779 mxmlNewText(description, 0, buffer));
1780
1781 if (structclass)
1782 {
1783 description = mxmlNewElement(structclass, "description");
1784 update_comment(structclass,
1785 mxmlNewText(description, 0, buffer));
1786 }
1787 else if (enumeration)
1788 {
1789 description = mxmlNewElement(enumeration, "description");
1790 update_comment(enumeration,
1791 mxmlNewText(description, 0, buffer));
1792 }
1793 }
1794
1795 typedefnode = NULL;
1796 }
1797 else if (strcmp(tree->value.element.name, "mxmldoc") &&
1798 !mxmlFindElement(tree, tree, "description",
1799 NULL, NULL, MXML_DESCEND_FIRST))
1800 {
1801 description = mxmlNewElement(tree, "description");
1802#ifdef DEBUG
1803 fprintf(stderr, " adding comment %p/%p to parent...\n",
1804 comment->last_child, comment->child);
1805#endif /* DEBUG */
1806 mxmlNewText(comment, 0, buffer);
1807 update_comment(tree,
1808 mxmlNewText(description, 0, buffer));
1809 }
1810 else
1811 {
1812#ifdef DEBUG
1813 fprintf(stderr, " before adding comment, child=%p, last_child=%p\n",
1814 comment->child, comment->last_child);
1815#endif /* DEBUG */
1816 mxmlNewText(comment, 0, buffer);
1817#ifdef DEBUG
1818 fprintf(stderr, " after adding comment, child=%p, last_child=%p\n",
1819 comment->child, comment->last_child);
1820#endif /* DEBUG */
1821 }
1822#ifdef DEBUG
1823 fprintf(stderr, "C comment: <<<< %s >>>\n", buffer);
1824#endif /* DEBUG */
1825
1826 state = STATE_NONE;
1827 break;
1828 }
1829 else
1830 ungetc(ch, fp);
1831 }
1832 else if (ch == '\n' && bufptr > buffer &&
1833 bufptr < (buffer + sizeof(buffer) - 1))
1834 *bufptr++ = ch;
1835 else if (!isspace(ch))
1836 break;
1837
1838 if (ch != EOF)
1839 ungetc(ch, fp);
1840
1841 if (bufptr > buffer && bufptr < (buffer + sizeof(buffer) - 1))
1842 *bufptr++ = '\n';
1843 break;
1844
1845 case '/' :
1846 if (ch == '/' && bufptr > buffer && bufptr[-1] == '*')
1847 {
1848 while (bufptr > buffer &&
1849 (bufptr[-1] == '*' || isspace(bufptr[-1] & 255)))
1850 bufptr --;
1851 *bufptr = '\0';
1852
1853 if (comment->child != comment->last_child)
1854 {
1855#ifdef DEBUG
1856 fprintf(stderr, " removing comment %p(%20.20s), last comment %p(%20.20s)...\n",
1857 comment->child,
1858 comment->child ? comment->child->value.text.string : "",
1859 comment->last_child,
1860 comment->last_child ? comment->last_child->value.text.string : "");
1861#endif /* DEBUG */
1862 mxmlDelete(comment->child);
1863#ifdef DEBUG
1864 fprintf(stderr, " new comment %p, last comment %p...\n",
1865 comment->child, comment->last_child);
1866#endif /* DEBUG */
1867 }
1868
1869#ifdef DEBUG
1870 fprintf(stderr,
1871 " processing comment, variable=%p, "
1872 "constant=%p, typedefnode=%p, tree=\"%s\"\n",
1873 variable, constant, typedefnode,
1874 tree->value.element.name);
1875#endif /* DEBUG */
1876
1877 if (variable)
1878 {
1879 if (strstr(buffer, "@private@"))
1880 {
1881 /*
1882 * Delete private variables...
1883 */
1884
1885 mxmlDelete(variable);
1886 }
1887 else
1888 {
1889 description = mxmlNewElement(variable, "description");
1890#ifdef DEBUG
1891 fprintf(stderr, " adding comment %p/%p to variable...\n",
1892 comment->last_child, comment->child);
1893#endif /* DEBUG */
1894 mxmlNewText(comment, 0, buffer);
1895 update_comment(variable,
1896 mxmlNewText(description, 0, buffer));
1897 }
1898
1899 variable = NULL;
1900 }
1901 else if (constant)
1902 {
1903 if (strstr(buffer, "@private@"))
1904 {
1905 /*
1906 * Delete private constants...
1907 */
1908
1909 mxmlDelete(constant);
1910 }
1911 else
1912 {
1913 description = mxmlNewElement(constant, "description");
1914#ifdef DEBUG
1915 fprintf(stderr, " adding comment %p/%p to constant...\n",
1916 comment->last_child, comment->child);
1917#endif /* DEBUG */
1918 mxmlNewText(comment, 0, buffer);
1919 update_comment(constant,
1920 mxmlNewText(description, 0, buffer));
1921 }
1922
1923 constant = NULL;
1924 }
1925 else if (typedefnode)
1926 {
1927 if (strstr(buffer, "@private@"))
1928 {
1929 /*
1930 * Delete private typedefs...
1931 */
1932
1933 mxmlDelete(typedefnode);
1934
1935 if (structclass)
1936 {
1937 mxmlDelete(structclass);
1938 structclass = NULL;
1939 }
1940
1941 if (enumeration)
1942 {
1943 mxmlDelete(enumeration);
1944 enumeration = NULL;
1945 }
1946 }
1947 else
1948 {
1949 description = mxmlNewElement(typedefnode, "description");
1950#ifdef DEBUG
1951 fprintf(stderr,
1952 " adding comment %p/%p to typedef %s...\n",
1953 comment->last_child, comment->child,
1954 mxmlElementGetAttr(typedefnode, "name"));
1955#endif /* DEBUG */
1956 mxmlNewText(comment, 0, buffer);
1957 update_comment(typedefnode,
1958 mxmlNewText(description, 0, buffer));
1959
1960 if (structclass)
1961 {
1962 description = mxmlNewElement(structclass, "description");
1963 update_comment(structclass,
1964 mxmlNewText(description, 0, buffer));
1965 }
1966 else if (enumeration)
1967 {
1968 description = mxmlNewElement(enumeration, "description");
1969 update_comment(enumeration,
1970 mxmlNewText(description, 0, buffer));
1971 }
1972 }
1973
1974 typedefnode = NULL;
1975 }
1976 else if (strcmp(tree->value.element.name, "mxmldoc") &&
1977 !mxmlFindElement(tree, tree, "description",
1978 NULL, NULL, MXML_DESCEND_FIRST))
1979 {
1980 description = mxmlNewElement(tree, "description");
1981#ifdef DEBUG
1982 fprintf(stderr, " adding comment %p/%p to parent...\n",
1983 comment->last_child, comment->child);
1984#endif /* DEBUG */
1985 mxmlNewText(comment, 0, buffer);
1986 update_comment(tree,
1987 mxmlNewText(description, 0, buffer));
1988 }
1989 else
1990 mxmlNewText(comment, 0, buffer);
1991
1992#ifdef DEBUG
1993 fprintf(stderr, "C comment: <<<< %s >>>\n", buffer);
1994#endif /* DEBUG */
1995
1996 state = STATE_NONE;
1997 break;
1998 }
1999
2000 default :
2001 if (ch == ' ' && bufptr == buffer)
2002 break;
2003
2004 if (bufptr < (buffer + sizeof(buffer) - 1))
2005 *bufptr++ = ch;
2006 break;
2007 }
2008 break;
2009
2010 case STATE_CXX_COMMENT : /* Inside a C++ comment */
2011 if (ch == '\n')
2012 {
2013 state = STATE_NONE;
2014 *bufptr = '\0';
2015
2016 if (comment->child != comment->last_child)
2017 {
2018#ifdef DEBUG
2019 fprintf(stderr, " removing comment %p(%20.20s), last comment %p(%20.20s)...\n",
2020 comment->child,
2021 comment->child ? comment->child->value.text.string : "",
2022 comment->last_child,
2023 comment->last_child ? comment->last_child->value.text.string : "");
2024#endif /* DEBUG */
2025 mxmlDelete(comment->child);
2026#ifdef DEBUG
2027 fprintf(stderr, " new comment %p, last comment %p...\n",
2028 comment->child, comment->last_child);
2029#endif /* DEBUG */
2030 }
2031
2032 if (variable)
2033 {
2034 if (strstr(buffer, "@private@"))
2035 {
2036 /*
2037 * Delete private variables...
2038 */
2039
2040 mxmlDelete(variable);
2041 }
2042 else
2043 {
2044 description = mxmlNewElement(variable, "description");
2045#ifdef DEBUG
2046 fprintf(stderr, " adding comment %p/%p to variable...\n",
2047 comment->last_child, comment->child);
2048#endif /* DEBUG */
2049 mxmlNewText(comment, 0, buffer);
2050 update_comment(variable,
2051 mxmlNewText(description, 0, buffer));
2052 }
2053
2054 variable = NULL;
2055 }
2056 else if (constant)
2057 {
2058 if (strstr(buffer, "@private@"))
2059 {
2060 /*
2061 * Delete private constants...
2062 */
2063
2064 mxmlDelete(constant);
2065 }
2066 else
2067 {
2068 description = mxmlNewElement(constant, "description");
2069#ifdef DEBUG
2070 fprintf(stderr, " adding comment %p/%p to constant...\n",
2071 comment->last_child, comment->child);
2072#endif /* DEBUG */
2073 mxmlNewText(comment, 0, buffer);
2074 update_comment(constant,
2075 mxmlNewText(description, 0, buffer));
2076 }
2077
2078 constant = NULL;
2079 }
2080 else if (typedefnode)
2081 {
2082 if (strstr(buffer, "@private@"))
2083 {
2084 /*
2085 * Delete private typedefs...
2086 */
2087
2088 mxmlDelete(typedefnode);
2089 typedefnode = NULL;
2090
2091 if (structclass)
2092 {
2093 mxmlDelete(structclass);
2094 structclass = NULL;
2095 }
2096
2097 if (enumeration)
2098 {
2099 mxmlDelete(enumeration);
2100 enumeration = NULL;
2101 }
2102 }
2103 else
2104 {
2105 description = mxmlNewElement(typedefnode, "description");
2106#ifdef DEBUG
2107 fprintf(stderr, " adding comment %p/%p to typedef %s...\n",
2108 comment->last_child, comment->child,
2109 mxmlElementGetAttr(typedefnode, "name"));
2110#endif /* DEBUG */
2111 mxmlNewText(comment, 0, buffer);
2112 update_comment(typedefnode,
2113 mxmlNewText(description, 0, buffer));
2114
2115 if (structclass)
2116 {
2117 description = mxmlNewElement(structclass, "description");
2118 update_comment(structclass,
2119 mxmlNewText(description, 0, buffer));
2120 }
2121 else if (enumeration)
2122 {
2123 description = mxmlNewElement(enumeration, "description");
2124 update_comment(enumeration,
2125 mxmlNewText(description, 0, buffer));
2126 }
2127 }
2128 }
2129 else if (strcmp(tree->value.element.name, "mxmldoc") &&
2130 !mxmlFindElement(tree, tree, "description",
2131 NULL, NULL, MXML_DESCEND_FIRST))
2132 {
2133 description = mxmlNewElement(tree, "description");
2134#ifdef DEBUG
2135 fprintf(stderr, " adding comment %p/%p to parent...\n",
2136 comment->last_child, comment->child);
2137#endif /* DEBUG */
2138 mxmlNewText(comment, 0, buffer);
2139 update_comment(tree,
2140 mxmlNewText(description, 0, buffer));
2141 }
2142 else
2143 mxmlNewText(comment, 0, buffer);
2144
2145#ifdef DEBUG
2146 fprintf(stderr, "C++ comment: <<<< %s >>>\n", buffer);
2147#endif /* DEBUG */
2148 }
2149 else if (ch == ' ' && bufptr == buffer)
2150 break;
2151 else if (bufptr < (buffer + sizeof(buffer) - 1))
2152 *bufptr++ = ch;
2153 break;
2154
2155 case STATE_STRING : /* Inside a string constant */
2156 *bufptr++ = ch;
2157
2158 if (ch == '\\')
2159 *bufptr++ = getc(fp);
2160 else if (ch == '\"')
2161 {
2162 *bufptr = '\0';
2163
2164 if (type)
2165 mxmlNewText(type, type->child != NULL, buffer);
2166
2167 state = STATE_NONE;
2168 }
2169 break;
2170
2171 case STATE_CHARACTER : /* Inside a character constant */
2172 *bufptr++ = ch;
2173
2174 if (ch == '\\')
2175 *bufptr++ = getc(fp);
2176 else if (ch == '\'')
2177 {
2178 *bufptr = '\0';
2179
2180 if (type)
2181 mxmlNewText(type, type->child != NULL, buffer);
2182
2183 state = STATE_NONE;
2184 }
2185 break;
2186
2187 case STATE_IDENTIFIER : /* Inside a keyword or identifier */
2188 if (isalnum(ch) || ch == '_' || ch == '[' || ch == ']' ||
2189 (ch == ',' && (parens > 1 || (type && !enumeration && !function))) ||
2190 ch == ':' || ch == '.' || ch == '~')
2191 {
2192 if (bufptr < (buffer + sizeof(buffer) - 1))
2193 *bufptr++ = ch;
2194 }
2195 else
2196 {
2197 ungetc(ch, fp);
2198 *bufptr = '\0';
2199 state = STATE_NONE;
2200
2201#ifdef DEBUG
2202 fprintf(stderr, " braces=%d, type=%p, type->child=%p, buffer=\"%s\"\n",
2203 braces, type, type ? type->child : NULL, buffer);
2204#endif /* DEBUG */
2205
2206 if (!braces)
2207 {
2208 if (!type || !type->child)
2209 {
2210 if (!strcmp(tree->value.element.name, "class"))
2211 {
2212 if (!strcmp(buffer, "public") ||
2213 !strcmp(buffer, "public:"))
2214 {
2215 scope = "public";
2216#ifdef DEBUG
2217 fputs(" scope = public\n", stderr);
2218#endif /* DEBUG */
2219 break;
2220 }
2221 else if (!strcmp(buffer, "private") ||
2222 !strcmp(buffer, "private:"))
2223 {
2224 scope = "private";
2225#ifdef DEBUG
2226 fputs(" scope = private\n", stderr);
2227#endif /* DEBUG */
2228 break;
2229 }
2230 else if (!strcmp(buffer, "protected") ||
2231 !strcmp(buffer, "protected:"))
2232 {
2233 scope = "protected";
2234#ifdef DEBUG
2235 fputs(" scope = protected\n", stderr);
2236#endif /* DEBUG */
2237 break;
2238 }
2239 }
2240 }
2241
2242 if (!type)
2243 type = mxmlNewElement(MXML_NO_PARENT, "type");
2244
2245#ifdef DEBUG
2246 fprintf(stderr, " function=%p (%s), type->child=%p, ch='%c', parens=%d\n",
2247 function,
2248 function ? mxmlElementGetAttr(function, "name") : "null",
2249 type->child, ch, parens);
2250#endif /* DEBUG */
2251
2252 if (!function && ch == '(')
2253 {
2254 if (type->child &&
2255 !strcmp(type->child->value.text.string, "extern"))
2256 {
2257 /*
2258 * Remove external declarations...
2259 */
2260
2261 mxmlDelete(type);
2262 type = NULL;
2263 break;
2264 }
2265
2266 if (type->child &&
2267 !strcmp(type->child->value.text.string, "static") &&
2268 !strcmp(tree->value.element.name, "mxmldoc"))
2269 {
2270 /*
2271 * Remove static functions...
2272 */
2273
2274 mxmlDelete(type);
2275 type = NULL;
2276 break;
2277 }
2278
2279 function = mxmlNewElement(MXML_NO_PARENT, "function");
2280 if ((bufptr = strchr(buffer, ':')) != NULL && bufptr[1] == ':')
2281 {
2282 *bufptr = '\0';
2283 bufptr += 2;
2284
2285 if ((fstructclass =
2286 mxmlFindElement(tree, tree, "class", "name", buffer,
2287 MXML_DESCEND_FIRST)) == NULL)
2288 fstructclass =
2289 mxmlFindElement(tree, tree, "struct", "name", buffer,
2290 MXML_DESCEND_FIRST);
2291 }
2292 else
2293 bufptr = buffer;
2294
2295 mxmlElementSetAttr(function, "name", bufptr);
2296
2297 if (scope)
2298 mxmlElementSetAttr(function, "scope", scope);
2299
2300#ifdef DEBUG
2301 fprintf(stderr, "function: %s\n", buffer);
2302 fprintf(stderr, " scope = %s\n", scope ? scope : "(null)");
2303 fprintf(stderr, " comment = %p\n", comment);
2304 fprintf(stderr, " child = (%p) %s\n",
2305 comment->child,
2306 comment->child ?
2307 comment->child->value.text.string : "(null)");
2308 fprintf(stderr, " last_child = (%p) %s\n",
2309 comment->last_child,
2310 comment->last_child ?
2311 comment->last_child->value.text.string : "(null)");
2312#endif /* DEBUG */
2313
2314 if (type->last_child &&
2315 strcmp(type->last_child->value.text.string, "void"))
2316 {
2317 returnvalue = mxmlNewElement(function, "returnvalue");
2318
2319 mxmlAdd(returnvalue, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, type);
2320
2321 description = mxmlNewElement(returnvalue, "description");
2322#ifdef DEBUG
2323 fprintf(stderr, " adding comment %p/%p to returnvalue...\n",
2324 comment->last_child, comment->child);
2325#endif /* DEBUG */
2326 update_comment(returnvalue, comment->last_child);
2327 mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT,
2328 comment->last_child);
2329 }
2330 else
2331 mxmlDelete(type);
2332
2333 description = mxmlNewElement(function, "description");
2334#ifdef DEBUG
2335 fprintf(stderr, " adding comment %p/%p to function...\n",
2336 comment->last_child, comment->child);
2337#endif /* DEBUG */
2338 update_comment(function, comment->last_child);
2339 mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT,
2340 comment->last_child);
2341
2342 type = NULL;
2343 }
2344 else if (function && ((ch == ')' && parens == 1) || ch == ','))
2345 {
2346 /*
2347 * Argument definition...
2348 */
2349
2350 if (strcmp(buffer, "void"))
2351 {
2352 mxmlNewText(type, type->child != NULL &&
2353 type->last_child->value.text.string[0] != '(' &&
2354 type->last_child->value.text.string[0] != '*',
2355 buffer);
2356
2357#ifdef DEBUG
2358 fprintf(stderr, "Argument: <<<< %s >>>\n", buffer);
2359#endif /* DEBUG */
2360
2361 variable = add_variable(function, "argument", type);
2362 }
2363 else
2364 mxmlDelete(type);
2365
2366 type = NULL;
2367 }
2368 else if (type->child && !function && (ch == ';' || ch == ','))
2369 {
2370#ifdef DEBUG
2371 fprintf(stderr, " got semicolon, typedefnode=%p, structclass=%p\n",
2372 typedefnode, structclass);
2373#endif /* DEBUG */
2374
2375 if (typedefnode || structclass)
2376 {
2377#ifdef DEBUG
2378 fprintf(stderr, "Typedef/struct/class: <<<< %s >>>>\n", buffer);
2379#endif /* DEBUG */
2380
2381 if (typedefnode)
2382 {
2383 mxmlElementSetAttr(typedefnode, "name", buffer);
2384
2385 sort_node(tree, typedefnode);
2386 }
2387
2388 if (structclass && !mxmlElementGetAttr(structclass, "name"))
2389 {
2390#ifdef DEBUG
2391 fprintf(stderr, "setting struct/class name to %s!\n",
2392 type->last_child->value.text.string);
2393#endif /* DEBUG */
2394 mxmlElementSetAttr(structclass, "name", buffer);
2395
2396 sort_node(tree, structclass);
2397 structclass = NULL;
2398 }
2399
2400 if (typedefnode)
2401 mxmlAdd(typedefnode, MXML_ADD_BEFORE, MXML_ADD_TO_PARENT,
2402 type);
2403 else
2404 mxmlDelete(type);
2405
2406 type = NULL;
2407 typedefnode = NULL;
2408 }
2409 else if (type->child &&
2410 !strcmp(type->child->value.text.string, "typedef"))
2411 {
2412 /*
2413 * Simple typedef...
2414 */
2415
2416#ifdef DEBUG
2417 fprintf(stderr, "Typedef: <<<< %s >>>\n", buffer);
2418#endif /* DEBUG */
2419
2420 typedefnode = mxmlNewElement(MXML_NO_PARENT, "typedef");
2421 mxmlElementSetAttr(typedefnode, "name", buffer);
2422 mxmlDelete(type->child);
2423
2424 sort_node(tree, typedefnode);
2425
2426 if (type->child)
2427 type->child->value.text.whitespace = 0;
2428
2429 mxmlAdd(typedefnode, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, type);
2430 type = NULL;
2431 }
2432 else if (!parens)
2433 {
2434 /*
2435 * Variable definition...
2436 */
2437
2438 if (type->child &&
2439 !strcmp(type->child->value.text.string, "static") &&
2440 !strcmp(tree->value.element.name, "mxmldoc"))
2441 {
2442 /*
2443 * Remove static functions...
2444 */
2445
2446 mxmlDelete(type);
2447 type = NULL;
2448 break;
2449 }
2450
2451 mxmlNewText(type, type->child != NULL &&
2452 type->last_child->value.text.string[0] != '(' &&
2453 type->last_child->value.text.string[0] != '*',
2454 buffer);
2455
2456#ifdef DEBUG
2457 fprintf(stderr, "Variable: <<<< %s >>>>\n", buffer);
2458 fprintf(stderr, " scope = %s\n", scope ? scope : "(null)");
2459#endif /* DEBUG */
2460
2461 variable = add_variable(MXML_NO_PARENT, "variable", type);
2462 type = NULL;
2463
2464 sort_node(tree, variable);
2465
2466 if (scope)
2467 mxmlElementSetAttr(variable, "scope", scope);
2468 }
2469 }
2470 else
2471 {
2472#ifdef DEBUG
2473 fprintf(stderr, "Identifier: <<<< %s >>>>\n", buffer);
2474#endif /* DEBUG */
2475
2476 mxmlNewText(type, type->child != NULL &&
2477 type->last_child->value.text.string[0] != '(' &&
2478 type->last_child->value.text.string[0] != '*',
2479 buffer);
2480 }
2481 }
2482 else if (enumeration && !isdigit(buffer[0] & 255))
2483 {
2484#ifdef DEBUG
2485 fprintf(stderr, "Constant: <<<< %s >>>\n", buffer);
2486#endif /* DEBUG */
2487
2488 constant = mxmlNewElement(MXML_NO_PARENT, "constant");
2489 mxmlElementSetAttr(constant, "name", buffer);
2490 sort_node(enumeration, constant);
2491 }
2492 else if (type)
2493 {
2494 mxmlDelete(type);
2495 type = NULL;
2496 }
2497 }
2498 break;
2499 }
2500
2501#if DEBUG > 1
2502 if (state != oldstate)
2503 {
2504 fprintf(stderr, " changed states from %s to %s on receipt of character '%c'...\n",
2505 states[oldstate], states[state], oldch);
2506 fprintf(stderr, " variable = %p\n", variable);
2507 if (type)
2508 {
2509 fputs(" type =", stderr);
2510 for (temp = type->child; temp; temp = temp->next)
2511 fprintf(stderr, " \"%s\"", temp->value.text.string);
2512 fputs("\n", stderr);
2513 }
2514 }
2515#endif /* DEBUG > 1 */
2516 }
2517
2518 mxmlDelete(comment);
2519
2520 /*
2521 * All done, return with no errors...
2522 */
2523
2524 return (0);
2525}
2526
2527
2528/*
2529 * 'sort_node()' - Insert a node sorted into a tree.
2530 */
2531
2532static void
2533sort_node(mxml_node_t *tree, /* I - Tree to sort into */
2534 mxml_node_t *node) /* I - Node to add */
2535{
2536 mxml_node_t *temp; /* Current node */
2537 const char *tempname, /* Name of current node */
2538 *nodename, /* Name of node */
2539 *scope; /* Scope */
2540
2541
2542#if DEBUG > 1
2543 fprintf(stderr, " sort_node(tree=%p, node=%p)\n", tree, node);
2544#endif /* DEBUG > 1 */
2545
2546 /*
2547 * Range check input...
2548 */
2549
2550 if (!tree || !node || node->parent == tree)
2551 return;
2552
2553 /*
2554 * Get the node name...
2555 */
2556
2557 if ((nodename = mxmlElementGetAttr(node, "name")) == NULL)
2558 return;
2559
2560 if (nodename[0] == '_')
2561 return; /* Hide private names */
2562
2563#if DEBUG > 1
2564 fprintf(stderr, " nodename=%p (\"%s\")\n", nodename, nodename);
2565#endif /* DEBUG > 1 */
2566
2567 /*
2568 * Delete any existing definition at this level, if one exists...
2569 */
2570
2571 if ((temp = mxmlFindElement(tree, tree, node->value.element.name,
2572 "name", nodename, MXML_DESCEND_FIRST)) != NULL)
2573 {
2574 /*
2575 * Copy the scope if needed...
2576 */
2577
2578 if ((scope = mxmlElementGetAttr(temp, "scope")) != NULL &&
2579 mxmlElementGetAttr(node, "scope") == NULL)
2580 {
2581#ifdef DEBUG
2582 fprintf(stderr, " copying scope %s for %s\n", scope, nodename);
2583#endif /* DEBUG */
2584
2585 mxmlElementSetAttr(node, "scope", scope);
2586 }
2587
2588 mxmlDelete(temp);
2589 }
2590
2591 /*
2592 * Add the node into the tree at the proper place...
2593 */
2594
2595 for (temp = tree->child; temp; temp = temp->next)
2596 {
2597#if DEBUG > 1
2598 fprintf(stderr, " temp=%p\n", temp);
2599#endif /* DEBUG > 1 */
2600
2601 if ((tempname = mxmlElementGetAttr(temp, "name")) == NULL)
2602 continue;
2603
2604#if DEBUG > 1
2605 fprintf(stderr, " tempname=%p (\"%s\")\n", tempname, tempname);
2606#endif /* DEBUG > 1 */
2607
2608 if (strcmp(nodename, tempname) < 0)
2609 break;
2610 }
2611
2612 if (temp)
2613 mxmlAdd(tree, MXML_ADD_BEFORE, temp, node);
2614 else
2615 mxmlAdd(tree, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node);
2616}
2617
2618
2619/*
2620 * 'update_comment()' - Update a comment node.
2621 */
2622
2623static void
2624update_comment(mxml_node_t *parent, /* I - Parent node */
2625 mxml_node_t *comment) /* I - Comment node */
2626{
2627 char *ptr; /* Pointer into comment */
2628
2629
2630#ifdef DEBUG
2631 fprintf(stderr, "update_comment(parent=%p, comment=%p)\n",
2632 parent, comment);
2633#endif /* DEBUG */
2634
2635 /*
2636 * Range check the input...
2637 */
2638
2639 if (!parent || !comment)
2640 return;
2641
2642 /*
2643 * Convert "\/" to "/"...
2644 */
2645
2646 for (ptr = strstr(comment->value.text.string, "\\/");
2647 ptr;
2648 ptr = strstr(ptr, "\\/"))
2649 safe_strcpy(ptr, ptr + 1);
2650
2651 /*
2652 * Update the comment...
2653 */
2654
2655 ptr = comment->value.text.string;
2656
2657 if (*ptr == '\'')
2658 {
2659 /*
2660 * Convert "'name()' - description" to "description".
2661 */
2662
2663 for (ptr ++; *ptr && *ptr != '\''; ptr ++);
2664
2665 if (*ptr == '\'')
2666 {
2667 ptr ++;
2668 while (isspace(*ptr & 255))
2669 ptr ++;
2670
2671 if (*ptr == '-')
2672 ptr ++;
2673
2674 while (isspace(*ptr & 255))
2675 ptr ++;
2676
2677 safe_strcpy(comment->value.text.string, ptr);
2678 }
2679 }
2680 else if (!strncmp(ptr, "I ", 2) || !strncmp(ptr, "O ", 2) ||
2681 !strncmp(ptr, "IO ", 3))
2682 {
2683 /*
2684 * 'Convert "I - description", "IO - description", or "O - description"
2685 * to description + direction attribute.
2686 */
2687
2688 ptr = strchr(ptr, ' ');
2689 *ptr++ = '\0';
2690
2691 if (!strcmp(parent->value.element.name, "argument"))
2692 mxmlElementSetAttr(parent, "direction", comment->value.text.string);
2693
2694 while (isspace(*ptr & 255))
2695 ptr ++;
2696
2697 if (*ptr == '-')
2698 ptr ++;
2699
2700 while (isspace(*ptr & 255))
2701 ptr ++;
2702
2703 safe_strcpy(comment->value.text.string, ptr);
2704 }
2705
2706 /*
2707 * Eliminate leading and trailing *'s...
2708 */
2709
2710 for (ptr = comment->value.text.string; *ptr == '*'; ptr ++);
2711 for (; isspace(*ptr & 255); ptr ++);
2712 if (ptr > comment->value.text.string)
2713 safe_strcpy(comment->value.text.string, ptr);
2714
2715 for (ptr = comment->value.text.string + strlen(comment->value.text.string) - 1;
2716 ptr > comment->value.text.string && *ptr == '*';
2717 ptr --)
2718 *ptr = '\0';
2719 for (; ptr > comment->value.text.string && isspace(*ptr & 255); ptr --)
2720 *ptr = '\0';
2721
2722#ifdef DEBUG
2723 fprintf(stderr, " updated comment = %s\n", comment->value.text.string);
2724#endif /* DEBUG */
2725}
2726
2727
2728/*
2729 * 'usage()' - Show program usage...
2730 */
2731
2732static void
2733usage(const char *option) /* I - Unknown option */
2734{
2735 if (option)
2736 printf("mxmldoc: Bad option \"%s\"!\n\n", option);
2737
2738 puts("Usage: mxmldoc [options] [filename.xml] [source files] >filename.html");
2739 puts("Options:");
2740 puts(" --css filename.css Set CSS stylesheet file");
2741 puts(" --docset bundleid.docset Generate documentation set");
2742 puts(" --docversion version Set documentation version");
2743 puts(" --feedname name Set documentation set feed name");
2744 puts(" --feedurl url Set documentation set feed URL");
2745 puts(" --footer footerfile Set footer file");
2746 puts(" --framed basename Generate framed HTML to basename*.html");
2747 puts(" --header headerfile Set header file");
2748 puts(" --intro introfile Set introduction file");
2749 puts(" --man name Generate man page");
2750 puts(" --no-output Do no generate documentation file");
2751 puts(" --section section Set section name");
2752 puts(" --title title Set documentation title");
2753 puts(" --tokens path Generate Xcode docset Tokens.xml file");
2754 puts(" --version Show mxmldoc/Mini-XML version");
2755
2756 exit(1);
2757}
2758
2759
2760/*
2761 * 'write_description()' - Write the description text.
2762 */
2763
2764static void
2765write_description(
2766 FILE *out, /* I - Output file */
2767 mxml_node_t *description, /* I - Description node */
2768 const char *element, /* I - HTML element, if any */
2769 int summary) /* I - Show summary */
2770{
2771 char text[10240], /* Text for description */
2772 *start, /* Start of code/link */
2773 *ptr; /* Pointer into text */
2774 int col; /* Current column */
2775
2776
2777 if (!description)
2778 return;
2779
2780 get_text(description, text, sizeof(text));
2781
2782 ptr = strstr(text, "\n\n");
2783
2784 if (summary)
2785 {
2786 if (ptr)
2787 *ptr = '\0';
2788
2789 ptr = text;
2790 }
2791 else if (!ptr || !ptr[2])
2792 return;
2793 else
2794 ptr += 2;
2795
2796 if (element && *element)
2797 fprintf(out, "<%s class=\"%s\">", element,
2798 summary ? "description" : "discussion");
2799 else if (!summary)
2800 fputs(".PP\n", out);
2801
2802 for (col = 0; *ptr; ptr ++)
2803 {
2804 if (*ptr == '@' &&
2805 (!strncmp(ptr + 1, "deprecated@", 11) ||
2806 !strncmp(ptr + 1, "since ", 6)))
2807 {
2808 ptr ++;
2809 while (*ptr && *ptr != '@')
2810 ptr ++;
2811
2812 if (!*ptr)
2813 return;
2814 }
2815 else if (!strncmp(ptr, "@code ", 6))
2816 {
2817 for (ptr += 6; isspace(*ptr & 255); ptr ++);
2818
2819 for (start = ptr, ptr ++; *ptr && *ptr != '@'; ptr ++);
2820
2821 if (*ptr)
2822 *ptr = '\0';
2823 else
2824 ptr --;
2825
2826 if (element && *element)
2827 fprintf(out, "<code>%s</code>", start);
2828 else if (element)
2829 fputs(start, out);
2830 else
2831 fprintf(out, "\\fB%s\\fR", start);
2832 }
2833 else if (!strncmp(ptr, "@link ", 6))
2834 {
2835 for (ptr += 6; isspace(*ptr & 255); ptr ++);
2836
2837 for (start = ptr, ptr ++; *ptr && *ptr != '@'; ptr ++);
2838
2839 if (*ptr)
2840 *ptr = '\0';
2841 else
2842 ptr --;
2843
2844 if (element && *element)
2845 fprintf(out, "<a href=\"#%s\"><code>%s</code></a>", start, start);
2846 else if (element)
2847 fputs(start, out);
2848 else
2849 fprintf(out, "\\fI%s\\fR", start);
2850 }
2851 else if (element)
2852 {
2853 if (*ptr == '&')
2854 fputs("&amp;", out);
2855 else if (*ptr == '<')
2856 fputs("&lt;", out);
2857 else if (*ptr == '>')
2858 fputs("&gt;", out);
2859 else if (*ptr == '\"')
2860 fputs("&quot;", out);
2861 else if (*ptr & 128)
2862 {
2863 /*
2864 * Convert UTF-8 to Unicode constant...
2865 */
2866
2867 int ch; /* Unicode character */
2868
2869
2870 ch = *ptr & 255;
2871
2872 if ((ch & 0xe0) == 0xc0)
2873 {
2874 ch = ((ch & 0x1f) << 6) | (ptr[1] & 0x3f);
2875 ptr ++;
2876 }
2877 else if ((ch & 0xf0) == 0xe0)
2878 {
2879 ch = ((((ch * 0x0f) << 6) | (ptr[1] & 0x3f)) << 6) | (ptr[2] & 0x3f);
2880 ptr += 2;
2881 }
2882
2883 if (ch == 0xa0)
2884 {
2885 /*
2886 * Handle non-breaking space as-is...
2887 */
2888
2889 fputs("&nbsp;", out);
2890 }
2891 else
2892 fprintf(out, "&#x%x;", ch);
2893 }
2894 else if (*ptr == '\n' && ptr[1] == '\n' && ptr[2] && ptr[2] != '@')
2895 {
2896 fputs("<br>\n<br>\n", out);
2897 ptr ++;
2898 }
2899 else
2900 putc(*ptr, out);
2901 }
2902 else if (*ptr == '\n' && ptr[1] == '\n' && ptr[2] && ptr[2] != '@')
2903 {
2904 fputs("\n.PP\n", out);
2905 ptr ++;
2906 }
2907 else
2908 {
2909 if (*ptr == '\\' || (*ptr == '.' && col == 0))
2910 putc('\\', out);
2911
2912 putc(*ptr, out);
2913
2914 if (*ptr == '\n')
2915 col = 0;
2916 else
2917 col ++;
2918 }
2919 }
2920
2921 if (element && *element)
2922 fprintf(out, "</%s>\n", element);
2923 else if (!element)
2924 putc('\n', out);
2925}
2926
2927
2928/*
2929 * 'write_element()' - Write an element's text nodes.
2930 */
2931
2932static void
2933write_element(FILE *out, /* I - Output file */
2934 mxml_node_t *doc, /* I - Document tree */
2935 mxml_node_t *element, /* I - Element to write */
2936 int mode) /* I - Output mode */
2937{
2938 mxml_node_t *node; /* Current node */
2939
2940
2941 if (!element)
2942 return;
2943
2944 for (node = element->child;
2945 node;
2946 node = mxmlWalkNext(node, element, MXML_NO_DESCEND))
2947 if (node->type == MXML_TEXT)
2948 {
2949 if (node->value.text.whitespace)
2950 putc(' ', out);
2951
2952 if (mode == OUTPUT_HTML &&
2953 (mxmlFindElement(doc, doc, "class", "name", node->value.text.string,
2954 MXML_DESCEND) ||
2955 mxmlFindElement(doc, doc, "enumeration", "name",
2956 node->value.text.string, MXML_DESCEND) ||
2957 mxmlFindElement(doc, doc, "struct", "name", node->value.text.string,
2958 MXML_DESCEND) ||
2959 mxmlFindElement(doc, doc, "typedef", "name", node->value.text.string,
2960 MXML_DESCEND) ||
2961 mxmlFindElement(doc, doc, "union", "name", node->value.text.string,
2962 MXML_DESCEND)))
2963 {
2964 fputs("<a href=\"#", out);
2965 write_string(out, node->value.text.string, mode);
2966 fputs("\">", out);
2967 write_string(out, node->value.text.string, mode);
2968 fputs("</a>", out);
2969 }
2970 else
2971 write_string(out, node->value.text.string, mode);
2972 }
2973
2974 if (!strcmp(element->value.element.name, "type") &&
2975 element->last_child->value.text.string[0] != '*')
2976 putc(' ', out);
2977}
2978
2979
2980/*
2981 * 'write_file()' - Copy a file to the output.
2982 */
2983
2984static void
2985write_file(FILE *out, /* I - Output file */
2986 const char *file) /* I - File to copy */
2987{
2988 FILE *fp; /* Copy file */
2989 char line[8192]; /* Line from file */
2990
2991
2992 if ((fp = fopen(file, "r")) == NULL)
2993 {
2994 fprintf(stderr, "mxmldoc: Unable to open \"%s\": %s\n", file,
2995 strerror(errno));
2996 return;
2997 }
2998
2999 while (fgets(line, sizeof(line), fp))
3000 fputs(line, out);
3001
3002 fclose(fp);
3003}
3004
3005
3006/*
3007 * 'write_function()' - Write documentation for a function.
3008 */
3009
3010static void
3011write_function(FILE *out, /* I - Output file */
3012 mxml_node_t *doc, /* I - Document */
3013 mxml_node_t *function, /* I - Function */
3014 int level) /* I - Base heading level */
3015{
3016 mxml_node_t *arg, /* Current argument */
3017 *adesc, /* Description of argument */
3018 *description, /* Description of function */
3019 *type, /* Type for argument */
3020 *node; /* Node in description */
3021 const char *name, /* Name of function/type */
3022 *defval; /* Default value */
3023 char prefix; /* Prefix character */
3024 char *sep; /* Newline separator */
3025
3026
3027 name = mxmlElementGetAttr(function, "name");
3028 description = mxmlFindElement(function, function, "description", NULL,
3029 NULL, MXML_DESCEND_FIRST);
3030
3031 fprintf(out, "<h%d class=\"%s\">%s<a name=\"%s\">%s</a></h%d>\n",
3032 level, level == 3 ? "function" : "method",
3033 get_comment_info(description), name, name, level);
3034
3035 if (description)
3036 write_description(out, description, "p", 1);
3037
3038 fputs("<p class=\"code\">\n", out);
3039
3040 arg = mxmlFindElement(function, function, "returnvalue", NULL,
3041 NULL, MXML_DESCEND_FIRST);
3042
3043 if (arg)
3044 write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL,
3045 NULL, MXML_DESCEND_FIRST),
3046 OUTPUT_HTML);
3047 else
3048 fputs("void ", out);
3049
3050 fprintf(out, "%s ", name);
3051 for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
3052 MXML_DESCEND_FIRST), prefix = '(';
3053 arg;
3054 arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
3055 MXML_NO_DESCEND), prefix = ',')
3056 {
3057 type = mxmlFindElement(arg, arg, "type", NULL, NULL,
3058 MXML_DESCEND_FIRST);
3059
3060 fprintf(out, "%c<br>\n&nbsp;&nbsp;&nbsp;&nbsp;", prefix);
3061 if (type->child)
3062 write_element(out, doc, type, OUTPUT_HTML);
3063
3064 fputs(mxmlElementGetAttr(arg, "name"), out);
3065 if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
3066 fprintf(out, " %s", defval);
3067 }
3068
3069 if (prefix == '(')
3070 fputs("(void);</p>\n", out);
3071 else
3072 {
3073 fprintf(out,
3074 "<br>\n);</p>\n"
3075 "<h%d class=\"parameters\">Parameters</h%d>\n"
3076 "<dl>\n", level + 1, level + 1);
3077
3078 for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
3079 MXML_DESCEND_FIRST);
3080 arg;
3081 arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
3082 MXML_NO_DESCEND))
3083 {
3084 fprintf(out, "<dt>%s</dt>\n", mxmlElementGetAttr(arg, "name"));
3085
3086 adesc = mxmlFindElement(arg, arg, "description", NULL, NULL,
3087 MXML_DESCEND_FIRST);
3088
3089 write_description(out, adesc, "dd", 1);
3090 write_description(out, adesc, "dd", 0);
3091 }
3092
3093 fputs("</dl>\n", out);
3094 }
3095
3096 arg = mxmlFindElement(function, function, "returnvalue", NULL,
3097 NULL, MXML_DESCEND_FIRST);
3098
3099 if (arg)
3100 {
3101 fprintf(out, "<h%d class=\"returnvalue\">Return Value</h%d>\n", level + 1,
3102 level + 1);
3103
3104 adesc = mxmlFindElement(arg, arg, "description", NULL, NULL,
3105 MXML_DESCEND_FIRST);
3106
3107 write_description(out, adesc, "p", 1);
3108 write_description(out, adesc, "p", 0);
3109 }
3110
3111 if (description)
3112 {
3113 for (node = description->child; node; node = node->next)
3114 if (node->value.text.string &&
3115 (sep = strstr(node->value.text.string, "\n\n")) != NULL)
3116 {
3117 sep += 2;
3118 if (*sep && strncmp(sep, "@since ", 7) &&
3119 strncmp(sep, "@deprecated@", 12))
3120 break;
3121 }
3122
3123 if (node)
3124 {
3125 fprintf(out, "<h%d class=\"discussion\">Discussion</h%d>\n", level + 1,
3126 level + 1);
3127 write_description(out, description, "p", 0);
3128 }
3129 }
3130}
3131
3132
3133/*
3134 * 'write_html()' - Write HTML documentation.
3135 */
3136
3137static void
3138write_html(const char *section, /* I - Section */
3139 const char *title, /* I - Title */
3140 const char *footerfile, /* I - Footer file */
3141 const char *headerfile, /* I - Header file */
3142 const char *introfile, /* I - Intro file */
3143 const char *cssfile, /* I - Stylesheet file */
3144 const char *framefile, /* I - Framed HTML basename */
3145 const char *docset, /* I - Documentation set directory */
3146 const char *docversion, /* I - Documentation set version */
3147 const char *feedname, /* I - Feed name for doc set */
3148 const char *feedurl, /* I - Feed URL for doc set */
3149 mxml_node_t *doc) /* I - XML documentation */
3150{
3151 FILE *out; /* Output file */
3152 mxml_node_t *function, /* Current function */
3153 *scut, /* Struct/class/union/typedef */
3154 *arg, /* Current argument */
3155 *description, /* Description of function/var */
3156 *type; /* Type for argument */
3157 const char *name, /* Name of function/type */
3158 *defval, /* Default value */
3159 *basename; /* Base filename for framed output */
3160 char filename[1024]; /* Current output filename */
3161
3162
3163 if (framefile)
3164 {
3165 /*
3166 * Get the basename of the frame file...
3167 */
3168
3169 if ((basename = strrchr(framefile, '/')) != NULL)
3170 basename ++;
3171 else
3172 basename = framefile;
3173
3174 if (strstr(basename, ".html"))
3175 fputs("mxmldoc: Frame base name should not contain .html extension!\n",
3176 stderr);
3177
3178 /*
3179 * Create the container HTML file for the frames...
3180 */
3181
3182 snprintf(filename, sizeof(filename), "%s.html", framefile);
3183
3184 if ((out = fopen(filename, "w")) == NULL)
3185 {
3186 fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
3187 strerror(errno));
3188 return;
3189 }
3190
3191 fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\" "
3192 "\"http://www.w3.org/TR/html4/frameset.dtd\">\n"
3193 "<html>\n"
3194 "<head>\n"
3195 "\t<title>", out);
3196 write_string(out, title, OUTPUT_HTML);
3197 fputs("</title>\n", out);
3198
3199 if (section)
3200 fprintf(out, "\t<meta name=\"keywords\" content=\"%s\">\n", section);
3201
3202 fputs("\t<meta http-equiv=\"Content-Type\" "
3203 "content=\"text/html;charset=utf-8\">\n"
3204 "\t<meta name=\"creator\" content=\"" MXML_VERSION "\">\n"
3205 "</head>\n", out);
3206
3207 fputs("<frameset cols=\"250,*\">\n", out);
3208 fprintf(out, "<frame src=\"%s-toc.html\">\n", basename);
3209 fprintf(out, "<frame name=\"body\" src=\"%s-body.html\">\n", basename);
3210 fputs("</frameset>\n"
3211 "<noframes>\n"
3212 "<h1>", out);
3213 write_string(out, title, OUTPUT_HTML);
3214 fprintf(out,
3215 "</h1>\n"
3216 "<ul>\n"
3217 "\t<li><a href=\"%s-toc.html\">Table of Contents</a></li>\n"
3218 "\t<li><a href=\"%s-body.html\">Body</a></li>\n"
3219 "</ul>\n", basename, basename);
3220 fputs("</noframes>\n"
3221 "</html>\n", out);
3222 fclose(out);
3223
3224 /*
3225 * Write the table-of-contents file...
3226 */
3227
3228 snprintf(filename, sizeof(filename), "%s-toc.html", framefile);
3229
3230 if ((out = fopen(filename, "w")) == NULL)
3231 {
3232 fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
3233 strerror(errno));
3234 return;
3235 }
3236
3237 write_html_head(out, section, title, cssfile);
3238
3239 snprintf(filename, sizeof(filename), "%s-body.html", basename);
3240
3241 fputs("<div class=\"contents\">\n", out);
3242 fprintf(out, "<h1 class=\"title\"><a href=\"%s\" target=\"body\">",
3243 filename);
3244 write_string(out, title, OUTPUT_HTML);
3245 fputs("</a></h1>\n", out);
3246
3247 write_toc(out, doc, introfile, filename, 0);
3248
3249 fputs("</div>\n"
3250 "</body>\n"
3251 "</html>\n", out);
3252 fclose(out);
3253
3254 /*
3255 * Finally, open the body file...
3256 */
3257
3258 snprintf(filename, sizeof(filename), "%s-body.html", framefile);
3259
3260 if ((out = fopen(filename, "w")) == NULL)
3261 {
3262 fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
3263 strerror(errno));
3264 return;
3265 }
3266 }
3267 else if (docset)
3268 {
3269 /*
3270 * Create an Xcode documentation set - start by removing any existing
3271 * output directory...
3272 */
3273
3274#ifdef __APPLE__
3275 const char *id; /* Identifier */
3276
3277
3278 if (!access(docset, 0) && !remove_directory(docset))
3279 return;
3280
3281 /*
3282 * Then make the Apple standard bundle directory structure...
3283 */
3284
3285 if (mkdir(docset, 0755))
3286 {
3287 fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", docset,
3288 strerror(errno));
3289 return;
3290 }
3291
3292 snprintf(filename, sizeof(filename), "%s/Contents", docset);
3293 if (mkdir(filename, 0755))
3294 {
3295 fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
3296 strerror(errno));
3297 return;
3298 }
3299
3300 snprintf(filename, sizeof(filename), "%s/Contents/Resources", docset);
3301 if (mkdir(filename, 0755))
3302 {
3303 fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
3304 strerror(errno));
3305 return;
3306 }
3307
3308 snprintf(filename, sizeof(filename), "%s/Contents/Resources/Documentation",
3309 docset);
3310 if (mkdir(filename, 0755))
3311 {
3312 fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
3313 strerror(errno));
3314 return;
3315 }
3316
3317 /*
3318 * The Info.plist file, which describes the documentation set...
3319 */
3320
3321 if ((id = strrchr(docset, '/')) != NULL)
3322 id ++;
3323 else
3324 id = docset;
3325
3326 snprintf(filename, sizeof(filename), "%s/Contents/Info.plist", docset);
3327 if ((out = fopen(filename, "w")) == NULL)
3328 {
3329 fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
3330 strerror(errno));
3331 return;
3332 }
3333
3334 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
3335 "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
3336 "<plist version=\"1.0\">\n"
3337 "<dict>\n"
3338 "\t<key>CFBundleIdentifier</key>\n"
3339 "\t<string>", out);
3340 write_string(out, id, OUTPUT_HTML);
3341 fputs("</string>\n"
3342 "\t<key>CFBundleName</key>\n"
3343 "\t<string>", out);
3344 write_string(out, title, OUTPUT_HTML);
3345 fputs("</string>\n"
3346 "\t<key>CFBundleVersion</key>\n"
3347 "\t<string>", out);
3348 write_string(out, docversion ? docversion : "0.0", OUTPUT_HTML);
3349 fputs("</string>\n"
3350 "\t<key>CFBundleShortVersionString</key>\n"
3351 "\t<string>", out);
3352 write_string(out, docversion ? docversion : "0.0", OUTPUT_HTML);
3353 fputs("</string>\n", out);
3354
3355 if (feedname)
3356 {
3357 fputs("\t<key>DocSetFeedName</key>\n"
3358 "\t<string>", out);
3359 write_string(out, feedname ? feedname : title, OUTPUT_HTML);
3360 fputs("</string>\n", out);
3361 }
3362
3363 if (feedurl)
3364 {
3365 fputs("\t<key>DocSetFeedURL</key>\n"
3366 "\t<string>", out);
3367 write_string(out, feedurl, OUTPUT_HTML);
3368 fputs("</string>\n", out);
3369 }
3370
3371 fputs("</dict>\n"
3372 "</plist>\n", out);
3373
3374 fclose(out);
3375
3376 /*
3377 * Next the Nodes.xml file...
3378 */
3379
3380 snprintf(filename, sizeof(filename), "%s/Contents/Resources/Nodes.xml",
3381 docset);
3382 if ((out = fopen(filename, "w")) == NULL)
3383 {
3384 fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
3385 strerror(errno));
3386 return;
3387 }
3388
3389 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
3390 "<DocSetNodes version=\"1.0\">\n"
3391 "<TOC>\n"
3392 "<Node id=\"0\">\n"
3393 "<Name>", out);
3394 write_string(out, title, OUTPUT_HTML);
3395 fputs("</Name>\n"
3396 "<Path>Documentation/index.html</Path>\n"
3397 "<Subnodes>\n", out);
3398
3399 write_toc(out, doc, introfile, NULL, 1);
3400
3401 fputs("</Subnodes>\n"
3402 "</Node>\n"
3403 "</TOC>\n"
3404 "</DocSetNodes>\n", out);
3405
3406 fclose(out);
3407
3408 /*
3409 * Then the Tokens.xml file...
3410 */
3411
3412 snprintf(filename, sizeof(filename), "%s/Contents/Resources/Tokens.xml",
3413 docset);
3414 if ((out = fopen(filename, "w")) == NULL)
3415 {
3416 fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
3417 strerror(errno));
3418 return;
3419 }
3420
3421 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
3422 "<Tokens version=\"1.0\">\n", out);
3423
3424 write_tokens(out, doc, "index.html");
3425
3426 fputs("</Tokens>\n", out);
3427
3428 fclose(out);
3429
3430 /*
3431 * Finally the HTML file...
3432 */
3433
3434 snprintf(filename, sizeof(filename),
3435 "%s/Contents/Resources/Documentation/index.html",
3436 docset);
3437 if ((out = fopen(filename, "w")) == NULL)
3438 {
3439 fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
3440 strerror(errno));
3441 return;
3442 }
3443
3444#else
3445 fputs("mxmldoc: Xcode documentation sets can only be created on "
3446 "Mac OS X.\n", stderr);
3447 return;
3448#endif /* __APPLE__ */
3449 }
3450 else
3451 out = stdout;
3452
3453 /*
3454 * Standard header...
3455 */
3456
3457 write_html_head(out, section, title, cssfile);
3458
3459 fputs("<div class='body'>\n", out);
3460
3461 /*
3462 * Header...
3463 */
3464
3465 if (headerfile)
3466 {
3467 /*
3468 * Use custom header...
3469 */
3470
3471 write_file(out, headerfile);
3472 }
3473 else
3474 {
3475 /*
3476 * Use standard header...
3477 */
3478
3479 fputs("<h1 class=\"title\">", out);
3480 write_string(out, title, OUTPUT_HTML);
3481 fputs("</h1>\n", out);
3482 }
3483
3484 /*
3485 * Table of contents...
3486 */
3487
3488 if (!framefile)
3489 write_toc(out, doc, introfile, NULL, 0);
3490
3491 /*
3492 * Intro...
3493 */
3494
3495 if (introfile)
3496 write_file(out, introfile);
3497
3498 /*
3499 * List of classes...
3500 */
3501
3502 if ((scut = find_public(doc, doc, "class")) != NULL)
3503 {
3504 fputs("<h2 class=\"title\"><a name=\"CLASSES\">Classes</a></h2>\n", out);
3505
3506 while (scut)
3507 {
3508 write_scu(out, doc, scut);
3509
3510 scut = find_public(scut, doc, "class");
3511 }
3512 }
3513
3514 /*
3515 * List of functions...
3516 */
3517
3518 if ((function = find_public(doc, doc, "function")) != NULL)
3519 {
3520 fputs("<h2 class=\"title\"><a name=\"FUNCTIONS\">Functions</a></h2>\n", out);
3521
3522 while (function)
3523 {
3524 write_function(out, doc, function, 3);
3525
3526 function = find_public(function, doc, "function");
3527 }
3528 }
3529
3530 /*
3531 * List of types...
3532 */
3533
3534 if ((scut = find_public(doc, doc, "typedef")) != NULL)
3535 {
3536 fputs("<h2 class=\"title\"><a name=\"TYPES\">Data Types</a></h2>\n", out);
3537
3538 while (scut)
3539 {
3540 name = mxmlElementGetAttr(scut, "name");
3541 description = mxmlFindElement(scut, scut, "description", NULL,
3542 NULL, MXML_DESCEND_FIRST);
3543 fprintf(out, "<h3 class=\"typedef\">%s<a name=\"%s\">%s</a></h3>\n",
3544 get_comment_info(description), name, name);
3545
3546 if (description)
3547 write_description(out, description, "p", 1);
3548
3549 fputs("<p class=\"code\">\n"
3550 "typedef ", out);
3551
3552 type = mxmlFindElement(scut, scut, "type", NULL, NULL,
3553 MXML_DESCEND_FIRST);
3554
3555 for (type = type->child; type; type = type->next)
3556 if (!strcmp(type->value.text.string, "("))
3557 break;
3558 else
3559 {
3560 if (type->value.text.whitespace)
3561 putc(' ', out);
3562
3563 if (mxmlFindElement(doc, doc, "class", "name",
3564 type->value.text.string, MXML_DESCEND) ||
3565 mxmlFindElement(doc, doc, "enumeration", "name",
3566 type->value.text.string, MXML_DESCEND) ||
3567 mxmlFindElement(doc, doc, "struct", "name",
3568 type->value.text.string, MXML_DESCEND) ||
3569 mxmlFindElement(doc, doc, "typedef", "name",
3570 type->value.text.string, MXML_DESCEND) ||
3571 mxmlFindElement(doc, doc, "union", "name",
3572 type->value.text.string, MXML_DESCEND))
3573 {
3574 fputs("<a href=\"#", out);
3575 write_string(out, type->value.text.string, OUTPUT_HTML);
3576 fputs("\">", out);
3577 write_string(out, type->value.text.string, OUTPUT_HTML);
3578 fputs("</a>", out);
3579 }
3580 else
3581 write_string(out, type->value.text.string, OUTPUT_HTML);
3582 }
3583
3584 if (type)
3585 {
3586 /*
3587 * Output function type...
3588 */
3589
3590 if (type->prev && type->prev->value.text.string[0] != '*')
3591 putc(' ', out);
3592
3593 fprintf(out, "(*%s", name);
3594
3595 for (type = type->next->next; type; type = type->next)
3596 {
3597 if (type->value.text.whitespace)
3598 putc(' ', out);
3599
3600 if (mxmlFindElement(doc, doc, "class", "name",
3601 type->value.text.string, MXML_DESCEND) ||
3602 mxmlFindElement(doc, doc, "enumeration", "name",
3603 type->value.text.string, MXML_DESCEND) ||
3604 mxmlFindElement(doc, doc, "struct", "name",
3605 type->value.text.string, MXML_DESCEND) ||
3606 mxmlFindElement(doc, doc, "typedef", "name",
3607 type->value.text.string, MXML_DESCEND) ||
3608 mxmlFindElement(doc, doc, "union", "name",
3609 type->value.text.string, MXML_DESCEND))
3610 {
3611 fputs("<a href=\"#", out);
3612 write_string(out, type->value.text.string, OUTPUT_HTML);
3613 fputs("\">", out);
3614 write_string(out, type->value.text.string, OUTPUT_HTML);
3615 fputs("</a>", out);
3616 }
3617 else
3618 write_string(out, type->value.text.string, OUTPUT_HTML);
3619 }
3620
3621 fputs(";\n", out);
3622 }
3623 else
3624 {
3625 type = mxmlFindElement(scut, scut, "type", NULL, NULL,
3626 MXML_DESCEND_FIRST);
3627 if (type->last_child->value.text.string[0] != '*')
3628 putc(' ', out);
3629
3630 fprintf(out, "%s;\n", name);
3631 }
3632
3633 fputs("</p>\n", out);
3634
3635 scut = find_public(scut, doc, "typedef");
3636 }
3637 }
3638
3639 /*
3640 * List of structures...
3641 */
3642
3643 if ((scut = find_public(doc, doc, "struct")) != NULL)
3644 {
3645 fputs("<h2 class=\"title\"><a name=\"STRUCTURES\">Structures</a></h2>\n",
3646 out);
3647
3648 while (scut)
3649 {
3650 write_scu(out, doc, scut);
3651
3652 scut = find_public(scut, doc, "struct");
3653 }
3654 }
3655
3656 /*
3657 * List of unions...
3658 */
3659
3660 if ((scut = find_public(doc, doc, "union")) != NULL)
3661 {
3662 fputs("<h2 class=\"title\"><a name=\"UNIONS\">Unions</a></h2>\n", out);
3663
3664 while (scut)
3665 {
3666 write_scu(out, doc, scut);
3667
3668 scut = find_public(scut, doc, "union");
3669 }
3670 }
3671
3672 /*
3673 * Variables...
3674 */
3675
3676 if ((arg = find_public(doc, doc, "variable")) != NULL)
3677 {
3678 fputs("<h2 class=\"title\"><a name=\"VARIABLES\">Variables</a></h2>\n",
3679 out);
3680
3681 while (arg)
3682 {
3683 name = mxmlElementGetAttr(arg, "name");
3684 description = mxmlFindElement(arg, arg, "description", NULL,
3685 NULL, MXML_DESCEND_FIRST);
3686 fprintf(out, "<h3 class=\"variable\">%s<a name=\"%s\">%s</a></h3>\n",
3687 get_comment_info(description), name, name);
3688
3689 if (description)
3690 write_description(out, description, "p", 1);
3691
3692 fputs("<p class=\"code\">", out);
3693
3694 write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL,
3695 NULL, MXML_DESCEND_FIRST),
3696 OUTPUT_HTML);
3697 fputs(mxmlElementGetAttr(arg, "name"), out);
3698 if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
3699 fprintf(out, " %s", defval);
3700 fputs(";</p>\n", out);
3701
3702 arg = find_public(arg, doc, "variable");
3703 }
3704 }
3705
3706 /*
3707 * List of enumerations...
3708 */
3709
3710 if ((scut = find_public(doc, doc, "enumeration")) != NULL)
3711 {
3712 fputs("<h2 class=\"title\"><a name=\"ENUMERATIONS\">Constants</a></h2>\n",
3713 out);
3714
3715 while (scut)
3716 {
3717 name = mxmlElementGetAttr(scut, "name");
3718 description = mxmlFindElement(scut, scut, "description", NULL,
3719 NULL, MXML_DESCEND_FIRST);
3720 fprintf(out, "<h3 class=\"enumeration\">%s<a name=\"%s\">%s</a></h3>\n",
3721 get_comment_info(description), name, name);
3722
3723 if (description)
3724 write_description(out, description, "p", 1);
3725
3726 fputs("<h4 class=\"constants\">Constants</h4>\n"
3727 "<dl>\n", out);
3728
3729 for (arg = mxmlFindElement(scut, scut, "constant", NULL, NULL,
3730 MXML_DESCEND_FIRST);
3731 arg;
3732 arg = mxmlFindElement(arg, scut, "constant", NULL, NULL,
3733 MXML_NO_DESCEND))
3734 {
3735 description = mxmlFindElement(arg, arg, "description", NULL,
3736 NULL, MXML_DESCEND_FIRST);
3737 fprintf(out, "<dt>%s %s</dt>\n",
3738 mxmlElementGetAttr(arg, "name"), get_comment_info(description));
3739
3740 write_description(out, description, "dd", 1);
3741 write_description(out, description, "dd", 0);
3742 }
3743
3744 fputs("</dl>\n", out);
3745
3746 scut = find_public(scut, doc, "enumeration");
3747 }
3748 }
3749
3750 /*
3751 * Footer...
3752 */
3753
3754 if (footerfile)
3755 {
3756 /*
3757 * Use custom footer...
3758 */
3759
3760 write_file(out, footerfile);
3761 }
3762
3763 fputs("</div>\n"
3764 "</body>\n"
3765 "</html>\n", out);
3766
3767 /*
3768 * Close output file as needed...
3769 */
3770
3771 if (out != stdout)
3772 fclose(out);
3773
3774#ifdef __APPLE__
3775 /*
3776 * When generating document sets, run the docsetutil program to index it...
3777 */
3778
3779 if (docset)
3780 {
3781 const char *args[4]; /* Argument array */
3782 pid_t pid; /* Process ID */
3783 int status; /* Exit status */
3784
3785
3786 args[0] = "/Developer/usr/bin/docsetutil";
3787 args[1] = "index";
3788 args[2] = docset;
3789 args[3] = NULL;
3790
3791 if (posix_spawn(&pid, args[0], NULL, NULL, (char **)args, environ))
3792 {
3793 fprintf(stderr, "mxmldoc: Unable to index documentation set \"%s\": %s\n",
3794 docset, strerror(errno));
3795 }
3796 else
3797 {
3798 while (wait(&status) != pid);
3799
3800 if (status)
3801 {
3802 if (WIFEXITED(status))
3803 fprintf(stderr, "mxmldoc: docsetutil exited with status %d\n",
3804 WEXITSTATUS(status));
3805 else
3806 fprintf(stderr, "mxmldoc: docsetutil crashed with signal %d\n",
3807 WTERMSIG(status));
3808 }
3809 else
3810 {
3811 /*
3812 * Remove unneeded temporary XML files...
3813 */
3814
3815 snprintf(filename, sizeof(filename), "%s/Contents/Resources/Nodes.xml",
3816 docset);
3817 unlink(filename);
3818
3819 snprintf(filename, sizeof(filename), "%s/Contents/Resources/Tokens.xml",
3820 docset);
3821 unlink(filename);
3822 }
3823 }
3824 }
3825#endif /* __APPLE__ */
3826}
3827
3828
3829/*
3830 * 'write_html_head()' - Write the standard HTML header.
3831 */
3832
3833static void
3834write_html_head(FILE *out, /* I - Output file */
3835 const char *section, /* I - Section */
3836 const char *title, /* I - Title */
3837 const char *cssfile) /* I - Stylesheet */
3838{
3839 fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" "
3840 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
3841 "<html>\n", out);
3842
3843 if (section)
3844 fprintf(out, "<!-- SECTION: %s -->\n", section);
3845
3846 fputs("<head>\n"
3847 "\t<title>", out);
3848 write_string(out, title, OUTPUT_HTML);
3849 fputs("\t</title>\n", out);
3850
3851 if (section)
3852 fprintf(out, "\t<meta name=\"keywords\" content=\"%s\">\n", section);
3853
3854 fputs("\t<meta http-equiv=\"Content-Type\" "
3855 "content=\"text/html;charset=utf-8\">\n"
3856 "\t<meta name=\"creator\" content=\"" MXML_VERSION "\">\n"
3857 "<style type=\"text/css\"><!--\n", out);
3858
3859 if (cssfile)
3860 {
3861 /*
3862 * Use custom stylesheet file...
3863 */
3864
3865 write_file(out, cssfile);
3866 }
3867 else
3868 {
3869 /*
3870 * Use standard stylesheet...
3871 */
3872
3873 fputs("body, p, h1, h2, h3, h4 {\n"
3874 " font-family: \"lucida grande\", geneva, helvetica, arial, "
3875 "sans-serif;\n"
3876 "}\n"
3877 "div.body h1 {\n"
3878 " font-size: 250%;\n"
3879 " font-weight: bold;\n"
3880 " margin: 0;\n"
3881 "}\n"
3882 "div.body h2 {\n"
3883 " font-size: 250%;\n"
3884 " margin-top: 1.5em;\n"
3885 "}\n"
3886 "div.body h3 {\n"
3887 " font-size: 150%;\n"
3888 " margin-bottom: 0.5em;\n"
3889 " margin-top: 1.5em;\n"
3890 "}\n"
3891 "div.body h4 {\n"
3892 " font-size: 110%;\n"
3893 " margin-bottom: 0.5em;\n"
3894 " margin-top: 1.5em;\n"
3895 "}\n"
3896 "div.body h5 {\n"
3897 " font-size: 100%;\n"
3898 " margin-bottom: 0.5em;\n"
3899 " margin-top: 1.5em;\n"
3900 "}\n"
3901 "div.contents {\n"
3902 " background: #e8e8e8;\n"
3903 " border: solid thin black;\n"
3904 " padding: 10px;\n"
3905 "}\n"
3906 "div.contents h1 {\n"
3907 " font-size: 110%;\n"
3908 "}\n"
3909 "div.contents h2 {\n"
3910 " font-size: 100%;\n"
3911 "}\n"
3912 "div.contents ul.contents {\n"
3913 " font-size: 80%;\n"
3914 "}\n"
3915 ".class {\n"
3916 " border-bottom: solid 2px gray;\n"
3917 "}\n"
3918 ".constants {\n"
3919 "}\n"
3920 ".description {\n"
3921 " margin-top: 0.5em;\n"
3922 "}\n"
3923 ".discussion {\n"
3924 "}\n"
3925 ".enumeration {\n"
3926 " border-bottom: solid 2px gray;\n"
3927 "}\n"
3928 ".function {\n"
3929 " border-bottom: solid 2px gray;\n"
3930 " margin-bottom: 0;\n"
3931 "}\n"
3932 ".members {\n"
3933 "}\n"
3934 ".method {\n"
3935 "}\n"
3936 ".parameters {\n"
3937 "}\n"
3938 ".returnvalue {\n"
3939 "}\n"
3940 ".struct {\n"
3941 " border-bottom: solid 2px gray;\n"
3942 "}\n"
3943 ".typedef {\n"
3944 " border-bottom: solid 2px gray;\n"
3945 "}\n"
3946 ".union {\n"
3947 " border-bottom: solid 2px gray;\n"
3948 "}\n"
3949 ".variable {\n"
3950 "}\n"
3951 "code, p.code, pre, ul.code li {\n"
3952 " font-family: monaco, courier, monospace;\n"
3953 " font-size: 90%;\n"
3954 "}\n"
3955 "a:link, a:visited {\n"
3956 " text-decoration: none;\n"
3957 "}\n"
3958 "span.info {\n"
3959 " background: black;\n"
3960 " border: solid thin black;\n"
3961 " color: white;\n"
3962 " font-size: 80%;\n"
3963 " font-style: italic;\n"
3964 " font-weight: bold;\n"
3965 " white-space: nowrap;\n"
3966 "}\n"
3967 "h3 span.info, h4 span.info {\n"
3968 " float: right;\n"
3969 " font-size: 100%;\n"
3970 "}\n"
3971 "ul.code, ul.contents, ul.subcontents {\n"
3972 " list-style-type: none;\n"
3973 " margin: 0;\n"
3974 " padding-left: 0;\n"
3975 "}\n"
3976 "ul.code li {\n"
3977 " margin: 0;\n"
3978 "}\n"
3979 "ul.contents > li {\n"
3980 " margin-top: 1em;\n"
3981 "}\n"
3982 "ul.contents li ul.code, ul.contents li ul.subcontents {\n"
3983 " padding-left: 2em;\n"
3984 "}\n"
3985 "div.body dl {\n"
3986 " margin-top: 0;\n"
3987 "}\n"
3988 "div.body dt {\n"
3989 " font-style: italic;\n"
3990 " margin-top: 0;\n"
3991 "}\n"
3992 "div.body dd {\n"
3993 " margin-bottom: 0.5em;\n"
3994 "}\n"
3995 "h1.title {\n"
3996 "}\n"
3997 "h2.title {\n"
3998 " border-bottom: solid 2px black;\n"
3999 "}\n"
4000 "h3.title {\n"
4001 " border-bottom: solid 2px black;\n"
4002 "}\n", out);
4003 }
4004
4005 fputs("--></style>\n"
4006 "</head>\n"
4007 "<body>\n", out);
4008}
4009
4010
4011/*
4012 * 'write_man()' - Write manpage documentation.
4013 */
4014
4015static void
4016write_man(const char *man_name, /* I - Name of manpage */
4017 const char *section, /* I - Section */
4018 const char *title, /* I - Title */
4019 const char *footerfile, /* I - Footer file */
4020 const char *headerfile, /* I - Header file */
4021 const char *introfile, /* I - Intro file */
4022 mxml_node_t *doc) /* I - XML documentation */
4023{
4024 int i; /* Looping var */
4025 mxml_node_t *function, /* Current function */
4026 *scut, /* Struct/class/union/typedef */
4027 *arg, /* Current argument */
4028 *description, /* Description of function/var */
4029 *type; /* Type for argument */
4030 const char *name, /* Name of function/type */
4031 *cname, /* Class name */
4032 *defval, /* Default value */
4033 *parent; /* Parent class */
4034 int inscope; /* Variable/method scope */
4035 char prefix; /* Prefix character */
4036 time_t curtime; /* Current time */
4037 struct tm *curdate; /* Current date */
4038 char buffer[1024]; /* String buffer */
4039 static const char * const scopes[] = /* Scope strings */
4040 {
4041 "private",
4042 "protected",
4043 "public"
4044 };
4045
4046
4047 /*
4048 * Standard man page...
4049 */
4050
4051 curtime = time(NULL);
4052 curdate = localtime(&curtime);
4053 strftime(buffer, sizeof(buffer), "%x", curdate);
4054
4055 printf(".TH %s %s \"%s\" \"%s\" \"%s\"\n", man_name, section ? section : "3",
4056 title ? title : "", buffer, title ? title : "");
4057
4058 /*
4059 * Header...
4060 */
4061
4062 if (headerfile)
4063 {
4064 /*
4065 * Use custom header...
4066 */
4067
4068 write_file(stdout, headerfile);
4069 }
4070 else
4071 {
4072 /*
4073 * Use standard header...
4074 */
4075
4076 puts(".SH NAME");
4077 printf("%s \\- %s\n", man_name, title ? title : man_name);
4078 }
4079
4080 /*
4081 * Intro...
4082 */
4083
4084 if (introfile)
4085 write_file(stdout, introfile);
4086
4087 /*
4088 * List of classes...
4089 */
4090
4091 if (find_public(doc, doc, "class"))
4092 {
4093 puts(".SH CLASSES");
4094
4095 for (scut = find_public(doc, doc, "class");
4096 scut;
4097 scut = find_public(scut, doc, "class"))
4098 {
4099 cname = mxmlElementGetAttr(scut, "name");
4100 description = mxmlFindElement(scut, scut, "description", NULL,
4101 NULL, MXML_DESCEND_FIRST);
4102 printf(".SS %s\n", cname);
4103
4104 write_description(stdout, description, NULL, 1);
4105
4106 printf(".PP\n"
4107 ".nf\n"
4108 "class %s", cname);
4109 if ((parent = mxmlElementGetAttr(scut, "parent")) != NULL)
4110 printf(" %s", parent);
4111 puts("\n{");
4112
4113 for (i = 0; i < 3; i ++)
4114 {
4115 inscope = 0;
4116
4117 for (arg = mxmlFindElement(scut, scut, "variable", "scope", scopes[i],
4118 MXML_DESCEND_FIRST);
4119 arg;
4120 arg = mxmlFindElement(arg, scut, "variable", "scope", scopes[i],
4121 MXML_NO_DESCEND))
4122 {
4123 if (!inscope)
4124 {
4125 inscope = 1;
4126 printf(" %s:\n", scopes[i]);
4127 }
4128
4129 printf(" ");
4130 write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL,
4131 NULL, MXML_DESCEND_FIRST),
4132 OUTPUT_MAN);
4133 printf("%s;\n", mxmlElementGetAttr(arg, "name"));
4134 }
4135
4136 for (function = mxmlFindElement(scut, scut, "function", "scope",
4137 scopes[i], MXML_DESCEND_FIRST);
4138 function;
4139 function = mxmlFindElement(function, scut, "function", "scope",
4140 scopes[i], MXML_NO_DESCEND))
4141 {
4142 if (!inscope)
4143 {
4144 inscope = 1;
4145 printf(" %s:\n", scopes[i]);
4146 }
4147
4148 name = mxmlElementGetAttr(function, "name");
4149
4150 printf(" ");
4151
4152 arg = mxmlFindElement(function, function, "returnvalue", NULL,
4153 NULL, MXML_DESCEND_FIRST);
4154
4155 if (arg)
4156 write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL,
4157 NULL, MXML_DESCEND_FIRST),
4158 OUTPUT_MAN);
4159 else if (strcmp(cname, name) && strcmp(cname, name + 1))
4160 fputs("void ", stdout);
4161
4162 printf("%s", name);
4163
4164 for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
4165 MXML_DESCEND_FIRST), prefix = '(';
4166 arg;
4167 arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
4168 MXML_NO_DESCEND), prefix = ',')
4169 {
4170 type = mxmlFindElement(arg, arg, "type", NULL, NULL,
4171 MXML_DESCEND_FIRST);
4172
4173 putchar(prefix);
4174 if (prefix == ',')
4175 putchar(' ');
4176
4177 if (type->child)
4178 write_element(stdout, doc, type, OUTPUT_MAN);
4179 fputs(mxmlElementGetAttr(arg, "name"), stdout);
4180 if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
4181 printf(" %s", defval);
4182 }
4183
4184 if (prefix == '(')
4185 puts("(void);");
4186 else
4187 puts(");");
4188 }
4189 }
4190
4191 puts("};\n"
4192 ".fi");
4193
4194 write_description(stdout, description, NULL, 0);
4195 }
4196 }
4197
4198 /*
4199 * List of enumerations...
4200 */
4201
4202 if (find_public(doc, doc, "enumeration"))
4203 {
4204 puts(".SH ENUMERATIONS");
4205
4206 for (scut = find_public(doc, doc, "enumeration");
4207 scut;
4208 scut = find_public(scut, doc, "enumeration"))
4209 {
4210 name = mxmlElementGetAttr(scut, "name");
4211 description = mxmlFindElement(scut, scut, "description", NULL,
4212 NULL, MXML_DESCEND_FIRST);
4213 printf(".SS %s\n", name);
4214
4215 write_description(stdout, description, NULL, 1);
4216 write_description(stdout, description, NULL, 0);
4217
4218 for (arg = mxmlFindElement(scut, scut, "constant", NULL, NULL,
4219 MXML_DESCEND_FIRST);
4220 arg;
4221 arg = mxmlFindElement(arg, scut, "constant", NULL, NULL,
4222 MXML_NO_DESCEND))
4223 {
4224 description = mxmlFindElement(arg, arg, "description", NULL,
4225 NULL, MXML_DESCEND_FIRST);
4226 printf(".TP 5\n%s\n.br\n", mxmlElementGetAttr(arg, "name"));
4227 write_description(stdout, description, NULL, 1);
4228 }
4229 }
4230 }
4231
4232 /*
4233 * List of functions...
4234 */
4235
4236 if (find_public(doc, doc, "function"))
4237 {
4238 puts(".SH FUNCTIONS");
4239
4240 for (function = find_public(doc, doc, "function");
4241 function;
4242 function = find_public(function, doc, "function"))
4243 {
4244 name = mxmlElementGetAttr(function, "name");
4245 description = mxmlFindElement(function, function, "description", NULL,
4246 NULL, MXML_DESCEND_FIRST);
4247 printf(".SS %s\n", name);
4248
4249 write_description(stdout, description, NULL, 1);
4250
4251 puts(".PP\n"
4252 ".nf");
4253
4254 arg = mxmlFindElement(function, function, "returnvalue", NULL,
4255 NULL, MXML_DESCEND_FIRST);
4256
4257 if (arg)
4258 write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL,
4259 NULL, MXML_DESCEND_FIRST),
4260 OUTPUT_MAN);
4261 else
4262 fputs("void", stdout);
4263
4264 printf(" %s ", name);
4265 for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
4266 MXML_DESCEND_FIRST), prefix = '(';
4267 arg;
4268 arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
4269 MXML_NO_DESCEND), prefix = ',')
4270 {
4271 type = mxmlFindElement(arg, arg, "type", NULL, NULL,
4272 MXML_DESCEND_FIRST);
4273
4274 printf("%c\n ", prefix);
4275 if (type->child)
4276 write_element(stdout, doc, type, OUTPUT_MAN);
4277 fputs(mxmlElementGetAttr(arg, "name"), stdout);
4278 if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
4279 printf(" %s", defval);
4280 }
4281
4282 if (prefix == '(')
4283 puts("(void);");
4284 else
4285 puts("\n);");
4286
4287 puts(".fi");
4288
4289 write_description(stdout, description, NULL, 0);
4290 }
4291 }
4292
4293 /*
4294 * List of structures...
4295 */
4296
4297 if (find_public(doc, doc, "struct"))
4298 {
4299 puts(".SH STRUCTURES");
4300
4301 for (scut = find_public(doc, doc, "struct");
4302 scut;
4303 scut = find_public(scut, doc, "struct"))
4304 {
4305 cname = mxmlElementGetAttr(scut, "name");
4306 description = mxmlFindElement(scut, scut, "description", NULL,
4307 NULL, MXML_DESCEND_FIRST);
4308 printf(".SS %s\n", cname);
4309
4310 write_description(stdout, description, NULL, 1);
4311
4312 printf(".PP\n"
4313 ".nf\n"
4314 "struct %s\n{\n", cname);
4315 for (arg = mxmlFindElement(scut, scut, "variable", NULL, NULL,
4316 MXML_DESCEND_FIRST);
4317 arg;
4318 arg = mxmlFindElement(arg, scut, "variable", NULL, NULL,
4319 MXML_NO_DESCEND))
4320 {
4321 printf(" ");
4322 write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL,
4323 NULL, MXML_DESCEND_FIRST),
4324 OUTPUT_MAN);
4325 printf("%s;\n", mxmlElementGetAttr(arg, "name"));
4326 }
4327
4328 for (function = mxmlFindElement(scut, scut, "function", NULL, NULL,
4329 MXML_DESCEND_FIRST);
4330 function;
4331 function = mxmlFindElement(function, scut, "function", NULL, NULL,
4332 MXML_NO_DESCEND))
4333 {
4334 name = mxmlElementGetAttr(function, "name");
4335
4336 printf(" ");
4337
4338 arg = mxmlFindElement(function, function, "returnvalue", NULL,
4339 NULL, MXML_DESCEND_FIRST);
4340
4341 if (arg)
4342 write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL,
4343 NULL, MXML_DESCEND_FIRST),
4344 OUTPUT_MAN);
4345 else if (strcmp(cname, name) && strcmp(cname, name + 1))
4346 fputs("void ", stdout);
4347
4348 fputs(name, stdout);
4349
4350 for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
4351 MXML_DESCEND_FIRST), prefix = '(';
4352 arg;
4353 arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
4354 MXML_NO_DESCEND), prefix = ',')
4355 {
4356 type = mxmlFindElement(arg, arg, "type", NULL, NULL,
4357 MXML_DESCEND_FIRST);
4358
4359 putchar(prefix);
4360 if (prefix == ',')
4361 putchar(' ');
4362
4363 if (type->child)
4364 write_element(stdout, doc, type, OUTPUT_MAN);
4365 fputs(mxmlElementGetAttr(arg, "name"), stdout);
4366 if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
4367 printf(" %s", defval);
4368 }
4369
4370 if (prefix == '(')
4371 puts("(void);");
4372 else
4373 puts(");");
4374 }
4375
4376 puts("};\n"
4377 ".fi");
4378
4379 write_description(stdout, description, NULL, 0);
4380 }
4381 }
4382
4383 /*
4384 * List of types...
4385 */
4386
4387 if (find_public(doc, doc, "typedef"))
4388 {
4389 puts(".SH TYPES");
4390
4391 for (scut = find_public(doc, doc, "typedef");
4392 scut;
4393 scut = find_public(scut, doc, "typedef"))
4394 {
4395 name = mxmlElementGetAttr(scut, "name");
4396 description = mxmlFindElement(scut, scut, "description", NULL,
4397 NULL, MXML_DESCEND_FIRST);
4398 printf(".SS %s\n", name);
4399
4400 write_description(stdout, description, NULL, 1);
4401
4402 fputs(".PP\n"
4403 ".nf\n"
4404 "typedef ", stdout);
4405
4406 type = mxmlFindElement(scut, scut, "type", NULL, NULL,
4407 MXML_DESCEND_FIRST);
4408
4409 for (type = type->child; type; type = type->next)
4410 if (!strcmp(type->value.text.string, "("))
4411 break;
4412 else
4413 {
4414 if (type->value.text.whitespace)
4415 putchar(' ');
4416
4417 write_string(stdout, type->value.text.string, OUTPUT_MAN);
4418 }
4419
4420 if (type)
4421 {
4422 /*
4423 * Output function type...
4424 */
4425
4426 printf(" (*%s", name);
4427
4428 for (type = type->next->next; type; type = type->next)
4429 {
4430 if (type->value.text.whitespace)
4431 putchar(' ');
4432
4433 write_string(stdout, type->value.text.string, OUTPUT_MAN);
4434 }
4435
4436 puts(";");
4437 }
4438 else
4439 printf(" %s;\n", name);
4440
4441 puts(".fi");
4442
4443 write_description(stdout, description, NULL, 0);
4444 }
4445 }
4446
4447 /*
4448 * List of unions...
4449 */
4450
4451 if (find_public(doc, doc, "union"))
4452 {
4453 puts(".SH UNIONS");
4454
4455 for (scut = find_public(doc, doc, "union");
4456 scut;
4457 scut = find_public(scut, doc, "union"))
4458 {
4459 name = mxmlElementGetAttr(scut, "name");
4460 description = mxmlFindElement(scut, scut, "description", NULL,
4461 NULL, MXML_DESCEND_FIRST);
4462 printf(".SS %s\n", name);
4463
4464 write_description(stdout, description, NULL, 1);
4465
4466 printf(".PP\n"
4467 ".nf\n"
4468 "union %s\n{\n", name);
4469 for (arg = mxmlFindElement(scut, scut, "variable", NULL, NULL,
4470 MXML_DESCEND_FIRST);
4471 arg;
4472 arg = mxmlFindElement(arg, scut, "variable", NULL, NULL,
4473 MXML_NO_DESCEND))
4474 {
4475 printf(" ");
4476 write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL,
4477 NULL, MXML_DESCEND_FIRST),
4478 OUTPUT_MAN);
4479 printf("%s;\n", mxmlElementGetAttr(arg, "name"));
4480 }
4481
4482 puts("};\n"
4483 ".fi");
4484
4485 write_description(stdout, description, NULL, 0);
4486 }
4487 }
4488
4489 /*
4490 * Variables...
4491 */
4492
4493 if (find_public(doc, doc, "variable"))
4494 {
4495 puts(".SH VARIABLES");
4496
4497 for (arg = find_public(doc, doc, "variable");
4498 arg;
4499 arg = find_public(arg, doc, "variable"))
4500 {
4501 name = mxmlElementGetAttr(arg, "name");
4502 description = mxmlFindElement(arg, arg, "description", NULL,
4503 NULL, MXML_DESCEND_FIRST);
4504 printf(".SS %s\n", name);
4505
4506 write_description(stdout, description, NULL, 1);
4507
4508 puts(".PP\n"
4509 ".nf");
4510
4511 write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL,
4512 NULL, MXML_DESCEND_FIRST),
4513 OUTPUT_MAN);
4514 fputs(mxmlElementGetAttr(arg, "name"), stdout);
4515 if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
4516 printf(" %s", defval);
4517 puts(";\n"
4518 ".fi");
4519
4520 write_description(stdout, description, NULL, 0);
4521 }
4522 }
4523
4524 if (footerfile)
4525 {
4526 /*
4527 * Use custom footer...
4528 */
4529
4530 write_file(stdout, footerfile);
4531 }
4532}
4533
4534
4535/*
4536 * 'write_scu()' - Write a structure, class, or union.
4537 */
4538
4539static void
4540write_scu(FILE *out, /* I - Output file */
4541 mxml_node_t *doc, /* I - Document */
4542 mxml_node_t *scut) /* I - Structure, class, or union */
4543{
4544 int i; /* Looping var */
4545 mxml_node_t *function, /* Current function */
4546 *arg, /* Current argument */
4547 *description, /* Description of function/var */
4548 *type; /* Type for argument */
4549 const char *name, /* Name of function/type */
4550 *cname, /* Class name */
4551 *defval, /* Default value */
4552 *parent, /* Parent class */
4553 *scope; /* Scope for variable/function */
4554 int inscope, /* Variable/method scope */
4555 maxscope; /* Maximum scope */
4556 char prefix; /* Prefix character */
4557 static const char * const scopes[] = /* Scope strings */
4558 {
4559 "private",
4560 "protected",
4561 "public"
4562 };
4563
4564
4565 cname = mxmlElementGetAttr(scut, "name");
4566 description = mxmlFindElement(scut, scut, "description", NULL,
4567 NULL, MXML_DESCEND_FIRST);
4568
4569 fprintf(out, "<h3 class=\"%s\">%s<a name=\"%s\">%s</a></h3>\n",
4570 scut->value.element.name, get_comment_info(description), cname,
4571 cname);
4572
4573 if (description)
4574 write_description(out, description, "p", 1);
4575
4576 fprintf(out, "<p class=\"code\">%s %s", scut->value.element.name, cname);
4577 if ((parent = mxmlElementGetAttr(scut, "parent")) != NULL)
4578 fprintf(out, " %s", parent);
4579 fputs(" {<br>\n", out);
4580
4581 maxscope = !strcmp(scut->value.element.name, "class") ? 3 : 1;
4582
4583 for (i = 0; i < maxscope; i ++)
4584 {
4585 inscope = maxscope == 1;
4586
4587 for (arg = mxmlFindElement(scut, scut, "variable", NULL, NULL,
4588 MXML_DESCEND_FIRST);
4589 arg;
4590 arg = mxmlFindElement(arg, scut, "variable", NULL, NULL,
4591 MXML_NO_DESCEND))
4592 {
4593 if (maxscope > 1 &&
4594 ((scope = mxmlElementGetAttr(arg, "scope")) == NULL ||
4595 strcmp(scope, scopes[i])))
4596 continue;
4597
4598 if (!inscope)
4599 {
4600 inscope = 1;
4601 fprintf(out, "&nbsp;&nbsp;%s:<br>\n", scopes[i]);
4602 }
4603
4604 fputs("&nbsp;&nbsp;&nbsp;&nbsp;", out);
4605 write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL,
4606 NULL, MXML_DESCEND_FIRST),
4607 OUTPUT_HTML);
4608 fprintf(out, "%s;<br>\n", mxmlElementGetAttr(arg, "name"));
4609 }
4610
4611 for (function = mxmlFindElement(scut, scut, "function", NULL, NULL,
4612 MXML_DESCEND_FIRST);
4613 function;
4614 function = mxmlFindElement(function, scut, "function", NULL, NULL,
4615 MXML_NO_DESCEND))
4616 {
4617 if (maxscope > 1 &&
4618 ((scope = mxmlElementGetAttr(arg, "scope")) == NULL ||
4619 strcmp(scope, scopes[i])))
4620 continue;
4621
4622 if (!inscope)
4623 {
4624 inscope = 1;
4625 fprintf(out, "&nbsp;&nbsp;%s:<br>\n", scopes[i]);
4626 }
4627
4628 name = mxmlElementGetAttr(function, "name");
4629
4630 fputs("&nbsp;&nbsp;&nbsp;&nbsp;", out);
4631
4632 arg = mxmlFindElement(function, function, "returnvalue", NULL,
4633 NULL, MXML_DESCEND_FIRST);
4634
4635 if (arg)
4636 write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL,
4637 NULL, MXML_DESCEND_FIRST),
4638 OUTPUT_HTML);
4639 else if (strcmp(cname, name) && strcmp(cname, name + 1))
4640 fputs("void ", out);
4641
4642 fprintf(out, "<a href=\"#%s.%s\">%s</a>", cname, name, name);
4643
4644 for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
4645 MXML_DESCEND_FIRST), prefix = '(';
4646 arg;
4647 arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
4648 MXML_NO_DESCEND), prefix = ',')
4649 {
4650 type = mxmlFindElement(arg, arg, "type", NULL, NULL,
4651 MXML_DESCEND_FIRST);
4652
4653 putc(prefix, out);
4654 if (prefix == ',')
4655 putc(' ', out);
4656
4657 if (type->child)
4658 write_element(out, doc, type, OUTPUT_HTML);
4659
4660 fputs(mxmlElementGetAttr(arg, "name"), out);
4661 if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
4662 fprintf(out, " %s", defval);
4663 }
4664
4665 if (prefix == '(')
4666 fputs("(void);<br>\n", out);
4667 else
4668 fputs(");<br>\n", out);
4669 }
4670 }
4671
4672 fputs("};</p>\n"
4673 "<h4 class=\"members\">Members</h4>\n"
4674 "<dl>\n", out);
4675
4676 for (arg = mxmlFindElement(scut, scut, "variable", NULL, NULL,
4677 MXML_DESCEND_FIRST);
4678 arg;
4679 arg = mxmlFindElement(arg, scut, "variable", NULL, NULL,
4680 MXML_NO_DESCEND))
4681 {
4682 description = mxmlFindElement(arg, arg, "description", NULL,
4683 NULL, MXML_DESCEND_FIRST);
4684
4685 fprintf(out, "<dt>%s %s</dt>\n",
4686 mxmlElementGetAttr(arg, "name"), get_comment_info(description));
4687
4688 write_description(out, description, "dd", 1);
4689 write_description(out, description, "dd", 0);
4690 }
4691
4692 fputs("</dl>\n", out);
4693
4694 for (function = mxmlFindElement(scut, scut, "function", NULL, NULL,
4695 MXML_DESCEND_FIRST);
4696 function;
4697 function = mxmlFindElement(function, scut, "function", NULL, NULL,
4698 MXML_NO_DESCEND))
4699 {
4700 write_function(out, doc, function, 4);
4701 }
4702}
4703
4704
4705/*
4706 * 'write_string()' - Write a string, quoting HTML special chars as needed.
4707 */
4708
4709static void
4710write_string(FILE *out, /* I - Output file */
4711 const char *s, /* I - String to write */
4712 int mode) /* I - Output mode */
4713{
4714 switch (mode)
4715 {
4716 case OUTPUT_HTML :
4717 case OUTPUT_XML :
4718 while (*s)
4719 {
4720 if (*s == '&')
4721 fputs("&amp;", out);
4722 else if (*s == '<')
4723 fputs("&lt;", out);
4724 else if (*s == '>')
4725 fputs("&gt;", out);
4726 else if (*s == '\"')
4727 fputs("&quot;", out);
4728 else if (*s & 128)
4729 {
4730 /*
4731 * Convert UTF-8 to Unicode constant...
4732 */
4733
4734 int ch; /* Unicode character */
4735
4736
4737 ch = *s & 255;
4738
4739 if ((ch & 0xe0) == 0xc0)
4740 {
4741 ch = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
4742 s ++;
4743 }
4744 else if ((ch & 0xf0) == 0xe0)
4745 {
4746 ch = ((((ch * 0x0f) << 6) | (s[1] & 0x3f)) << 6) | (s[2] & 0x3f);
4747 s += 2;
4748 }
4749
4750 if (ch == 0xa0)
4751 {
4752 /*
4753 * Handle non-breaking space as-is...
4754 */
4755
4756 fputs("&nbsp;", out);
4757 }
4758 else
4759 fprintf(out, "&#x%x;", ch);
4760 }
4761 else
4762 putc(*s, out);
4763
4764 s ++;
4765 }
4766 break;
4767
4768 case OUTPUT_MAN :
4769 while (*s)
4770 {
4771 if (*s == '\\' || *s == '-')
4772 putc('\\', out);
4773
4774 putc(*s++, out);
4775 }
4776 break;
4777 }
4778}
4779
4780
4781/*
4782 * 'write_toc()' - Write a table-of-contents.
4783 */
4784
4785static void
4786write_toc(FILE *out, /* I - Output file */
4787 mxml_node_t *doc, /* I - Document */
4788 const char *introfile, /* I - Introduction file */
4789 const char *target, /* I - Target name */
4790 int xml) /* I - Write XML nodes? */
4791{
4792 FILE *fp; /* Intro file */
4793 mxml_node_t *function, /* Current function */
4794 *scut, /* Struct/class/union/typedef */
4795 *arg, /* Current argument */
4796 *description; /* Description of function/var */
4797 const char *name, /* Name of function/type */
4798 *targetattr; /* Target attribute, if any */
4799 int xmlid = 1; /* Current XML node ID */
4800
4801
4802 /*
4803 * If target is set, it is the frame file that contains the body.
4804 * Otherwise, we are creating a single-file...
4805 */
4806
4807 if (target)
4808 targetattr = " target=\"body\"";
4809 else
4810 targetattr = "";
4811
4812 /*
4813 * The table-of-contents is a nested unordered list. Start by
4814 * reading any intro file to see if there are any headings there.
4815 */
4816
4817 if (!xml)
4818 fputs("<h2 class=\"title\">Contents</h2>\n"
4819 "<ul class=\"contents\">\n", out);
4820
4821 if (introfile && (fp = fopen(introfile, "r")) != NULL)
4822 {
4823 char line[8192], /* Line from file */
4824 *ptr, /* Pointer in line */
4825 *end, /* End of line */
4826 *anchor, /* Anchor name */
4827 quote, /* Quote character for value */
4828 level = '2', /* Current heading level */
4829 newlevel; /* New heading level */
4830 int inelement; /* In an element? */
4831
4832
4833 while (fgets(line, sizeof(line), fp))
4834 {
4835 /*
4836 * See if this line has a heading...
4837 */
4838
4839 if ((ptr = strstr(line, "<h")) == NULL &&
4840 (ptr = strstr(line, "<H")) == NULL)
4841 continue;
4842
4843 if (ptr[2] != '2' && ptr[2] != '3')
4844 continue;
4845
4846 newlevel = ptr[2];
4847
4848 /*
4849 * Make sure we have the whole heading...
4850 */
4851
4852 while (!strstr(line, "</h") && !strstr(line, "</H"))
4853 {
4854 end = line + strlen(line);
4855
4856 if (end == (line + sizeof(line) - 1) ||
4857 !fgets(end, (int)(sizeof(line) - (end - line)), fp))
4858 break;
4859 }
4860
4861 /*
4862 * Convert newlines and tabs to spaces...
4863 */
4864
4865 for (ptr = line; *ptr; ptr ++)
4866 if (isspace(*ptr & 255))
4867 *ptr = ' ';
4868
4869 /*
4870 * Find the anchor and text...
4871 */
4872
4873 for (ptr = strchr(line, '<'); ptr; ptr = strchr(ptr + 1, '<'))
4874 if (!strncmp(ptr, "<A NAME=", 8) || !strncmp(ptr, "<a name=", 8))
4875 break;
4876
4877 if (!ptr)
4878 continue;
4879
4880 ptr += 8;
4881 inelement = 1;
4882
4883 if (*ptr == '\'' || *ptr == '\"')
4884 {
4885 /*
4886 * Quoted anchor...
4887 */
4888
4889 quote = *ptr++;
4890 anchor = ptr;
4891
4892 while (*ptr && *ptr != quote)
4893 ptr ++;
4894
4895 if (!*ptr)
4896 continue;
4897
4898 *ptr++ = '\0';
4899 }
4900 else
4901 {
4902 /*
4903 * Non-quoted anchor...
4904 */
4905
4906 anchor = ptr;
4907
4908 while (*ptr && *ptr != '>' && !isspace(*ptr & 255))
4909 ptr ++;
4910
4911 if (!*ptr)
4912 continue;
4913
4914 if (*ptr == '>')
4915 inelement = 0;
4916
4917 *ptr++ = '\0';
4918 }
4919
4920 /*
4921 * Write text until we see "</A>"...
4922 */
4923
4924 if (xml)
4925 {
4926 if (newlevel < level)
4927 fputs("</Node>\n"
4928 "</Subnodes></Node>\n", out);
4929 else if (newlevel > level && newlevel == '3')
4930 fputs("<Subnodes>\n", out);
4931 else if (xmlid > 1)
4932 fputs("</Node>\n", out);
4933
4934 level = newlevel;
4935
4936 fprintf(out, "<Node id=\"%d\">\n"
4937 "<Path>Documentation/index.html</Path>\n"
4938 "<Anchor>%s</Anchor>\n"
4939 "<Name>", xmlid ++, anchor);
4940
4941 quote = 0;
4942
4943 while (*ptr)
4944 {
4945 if (inelement)
4946 {
4947 if (*ptr == quote)
4948 quote = 0;
4949 else if (*ptr == '>')
4950 inelement = 0;
4951 else if (*ptr == '\'' || *ptr == '\"')
4952 quote = *ptr;
4953 }
4954 else if (*ptr == '<')
4955 {
4956 if (!strncmp(ptr, "</A>", 4) || !strncmp(ptr, "</a>", 4))
4957 break;
4958
4959 inelement = 1;
4960 }
4961 else
4962 putc(*ptr, out);
4963
4964 ptr ++;
4965 }
4966
4967 fputs("</Name>\n", out);
4968 }
4969 else
4970 {
4971 if (newlevel < level)
4972 fputs("</li>\n"
4973 "</ul></li>\n", out);
4974 else if (newlevel > level)
4975 fputs("<ul class=\"subcontents\">\n", out);
4976 else if (xmlid > 1)
4977 fputs("</li>\n", out);
4978
4979 level = newlevel;
4980 xmlid ++;
4981
4982 fprintf(out, "%s<li><a href=\"%s#%s\"%s>", level > '2' ? "\t" : "",
4983 target ? target : "", anchor, targetattr);
4984
4985 quote = 0;
4986
4987 while (*ptr)
4988 {
4989 if (inelement)
4990 {
4991 if (*ptr == quote)
4992 quote = 0;
4993 else if (*ptr == '>')
4994 inelement = 0;
4995 else if (*ptr == '\'' || *ptr == '\"')
4996 quote = *ptr;
4997 }
4998 else if (*ptr == '<')
4999 {
5000 if (!strncmp(ptr, "</A>", 4) || !strncmp(ptr, "</a>", 4))
5001 break;
5002
5003 inelement = 1;
5004 }
5005 else
5006 putc(*ptr, out);
5007
5008 ptr ++;
5009 }
5010
5011 fputs("</a>", out);
5012 }
5013 }
5014
5015 if (level > '1')
5016 {
5017 if (xml)
5018 {
5019 fputs("</Node>\n", out);
5020
5021 if (level == '3')
5022 fputs("</Subnodes></Node>\n", out);
5023 }
5024 else
5025 {
5026 fputs("</li>\n", out);
5027
5028 if (level == '3')
5029 fputs("</ul></li>\n", out);
5030 }
5031 }
5032
5033 fclose(fp);
5034 }
5035
5036 /*
5037 * Next the classes...
5038 */
5039
5040 if ((scut = find_public(doc, doc, "class")) != NULL)
5041 {
5042 if (xml)
5043 fprintf(out, "<Node id=\"%d\">\n"
5044 "<Path>Documentation/index.html</Path>\n"
5045 "<Anchor>CLASSES</Anchor>\n"
5046 "<Name>Classes</Name>\n"
5047 "<Subnodes>\n", xmlid ++);
5048 else
5049 fprintf(out, "<li><a href=\"%s#CLASSES\"%s>Classes</a>"
5050 "<ul class=\"code\">\n",
5051 target ? target : "", targetattr);
5052
5053 while (scut)
5054 {
5055 name = mxmlElementGetAttr(scut, "name");
5056 description = mxmlFindElement(scut, scut, "description",
5057 NULL, NULL, MXML_DESCEND_FIRST);
5058
5059 if (xml)
5060 {
5061 fprintf(out, "<Node id=\"%d\">\n"
5062 "<Path>Documentation/index.html</Path>\n"
5063 "<Anchor>%s</Anchor>\n"
5064 "<Name>%s</Name>\n"
5065 "</Node>\n", xmlid ++, name, name);
5066 }
5067 else
5068 {
5069 fprintf(out, "\t<li><a href=\"%s#%s\"%s title=\"",
5070 target ? target : "", name, targetattr);
5071 write_description(out, description, "", 1);
5072 fprintf(out, "\">%s</a></li>\n", name);
5073 }
5074
5075 scut = find_public(scut, doc, "class");
5076 }
5077
5078 if (xml)
5079 fputs("</Subnodes></Node>\n", out);
5080 else
5081 fputs("</ul></li>\n", out);
5082 }
5083
5084 /*
5085 * Functions...
5086 */
5087
5088 if ((function = find_public(doc, doc, "function")) != NULL)
5089 {
5090 if (xml)
5091 fprintf(out, "<Node id=\"%d\">\n"
5092 "<Path>Documentation/index.html</Path>\n"
5093 "<Anchor>FUNCTIONS</Anchor>\n"
5094 "<Name>Functions</Name>\n"
5095 "<Subnodes>\n", xmlid ++);
5096 else
5097 fprintf(out, "<li><a href=\"%s#FUNCTIONS\"%s>Functions</a>"
5098 "<ul class=\"code\">\n", target ? target : "", targetattr);
5099
5100 while (function)
5101 {
5102 name = mxmlElementGetAttr(function, "name");
5103 description = mxmlFindElement(function, function, "description",
5104 NULL, NULL, MXML_DESCEND_FIRST);
5105
5106 if (xml)
5107 {
5108 fprintf(out, "<Node id=\"%d\">\n"
5109 "<Path>Documentation/index.html</Path>\n"
5110 "<Anchor>%s</Anchor>\n"
5111 "<Name>%s</Name>\n"
5112 "</Node>\n", xmlid ++, name, name);
5113 }
5114 else
5115 {
5116 fprintf(out, "\t<li><a href=\"%s#%s\"%s title=\"",
5117 target ? target : "", name, targetattr);
5118 write_description(out, description, "", 1);
5119 fprintf(out, "\">%s</a></li>\n", name);
5120 }
5121
5122 function = find_public(function, doc, "function");
5123 }
5124
5125 if (xml)
5126 fputs("</Subnodes></Node>\n", out);
5127 else
5128 fputs("</ul></li>\n", out);
5129 }
5130
5131 /*
5132 * Data types...
5133 */
5134
5135 if ((scut = find_public(doc, doc, "typedef")) != NULL)
5136 {
5137 if (xml)
5138 fprintf(out, "<Node id=\"%d\">\n"
5139 "<Path>Documentation/index.html</Path>\n"
5140 "<Anchor>TYPES</Anchor>\n"
5141 "<Name>Data Types</Name>\n"
5142 "<Subnodes>\n", xmlid ++);
5143 else
5144 fprintf(out, "<li><a href=\"%s#TYPES\"%s>Data Types</a>"
5145 "<ul class=\"code\">\n", target ? target : "", targetattr);
5146
5147 while (scut)
5148 {
5149 name = mxmlElementGetAttr(scut, "name");
5150 description = mxmlFindElement(scut, scut, "description",
5151 NULL, NULL, MXML_DESCEND_FIRST);
5152
5153 if (xml)
5154 {
5155 fprintf(out, "<Node id=\"%d\">\n"
5156 "<Path>Documentation/index.html</Path>\n"
5157 "<Anchor>%s</Anchor>\n"
5158 "<Name>%s</Name>\n"
5159 "</Node>\n", xmlid ++, name, name);
5160 }
5161 else
5162 {
5163 fprintf(out, "\t<li><a href=\"%s#%s\"%s title=\"",
5164 target ? target : "", name, targetattr);
5165 write_description(out, description, "", 1);
5166 fprintf(out, "\">%s</a></li>\n", name);
5167 }
5168
5169 scut = find_public(scut, doc, "typedef");
5170 }
5171
5172 if (xml)
5173 fputs("</Subnodes></Node>\n", out);
5174 else
5175 fputs("</ul></li>\n", out);
5176 }
5177
5178 /*
5179 * Structures...
5180 */
5181
5182 if ((scut = find_public(doc, doc, "struct")) != NULL)
5183 {
5184 if (xml)
5185 fprintf(out, "<Node id=\"%d\">\n"
5186 "<Path>Documentation/index.html</Path>\n"
5187 "<Anchor>STRUCTURES</Anchor>\n"
5188 "<Name>Structures</Name>\n"
5189 "<Subnodes>\n", xmlid ++);
5190 else
5191 fprintf(out, "<li><a href=\"%s#STRUCTURES\"%s>Structures</a>"
5192 "<ul class=\"code\">\n", target ? target : "", targetattr);
5193
5194 while (scut)
5195 {
5196 name = mxmlElementGetAttr(scut, "name");
5197 description = mxmlFindElement(scut, scut, "description",
5198 NULL, NULL, MXML_DESCEND_FIRST);
5199
5200 if (xml)
5201 {
5202 fprintf(out, "<Node id=\"%d\">\n"
5203 "<Path>Documentation/index.html</Path>\n"
5204 "<Anchor>%s</Anchor>\n"
5205 "<Name>%s</Name>\n"
5206 "</Node>\n", xmlid ++, name, name);
5207 }
5208 else
5209 {
5210 fprintf(out, "\t<li><a href=\"%s#%s\"%s title=\"",
5211 target ? target : "", name, targetattr);
5212 write_description(out, description, "", 1);
5213 fprintf(out, "\">%s</a></li>\n", name);
5214 }
5215
5216 scut = find_public(scut, doc, "struct");
5217 }
5218
5219 if (xml)
5220 fputs("</Subnodes></Node>\n", out);
5221 else
5222 fputs("</ul></li>\n", out);
5223 }
5224
5225 /*
5226 * Unions...
5227 */
5228
5229 if ((scut = find_public(doc, doc, "union")) != NULL)
5230 {
5231 if (xml)
5232 fprintf(out, "<Node id=\"%d\">\n"
5233 "<Path>Documentation/index.html</Path>\n"
5234 "<Anchor>UNIONS</Anchor>\n"
5235 "<Name>Unions</Name>\n"
5236 "<Subnodes>\n", xmlid ++);
5237 else
5238 fprintf(out,
5239 "<li><a href=\"%s#UNIONS\"%s>Unions</a><ul class=\"code\">\n",
5240 target ? target : "", targetattr);
5241
5242 while (scut)
5243 {
5244 name = mxmlElementGetAttr(scut, "name");
5245 description = mxmlFindElement(scut, scut, "description",
5246 NULL, NULL, MXML_DESCEND_FIRST);
5247
5248 if (xml)
5249 {
5250 fprintf(out, "<Node id=\"%d\">\n"
5251 "<Path>Documentation/index.html</Path>\n"
5252 "<Anchor>%s</Anchor>\n"
5253 "<Name>%s</Name>\n"
5254 "</Node>\n", xmlid ++, name, name);
5255 }
5256 else
5257 {
5258 fprintf(out, "\t<li><a href=\"%s#%s\"%s title=\"",
5259 target ? target : "", name, targetattr);
5260 write_description(out, description, "", 1);
5261 fprintf(out, "\">%s</a></li>\n", name);
5262 }
5263
5264 scut = find_public(scut, doc, "union");
5265 }
5266
5267 if (xml)
5268 fputs("</Subnodes></Node>\n", out);
5269 else
5270 fputs("</ul></li>\n", out);
5271 }
5272
5273 /*
5274 * Globals variables...
5275 */
5276
5277 if ((arg = find_public(doc, doc, "variable")) != NULL)
5278 {
5279 if (xml)
5280 fprintf(out, "<Node id=\"%d\">\n"
5281 "<Path>Documentation/index.html</Path>\n"
5282 "<Anchor>VARIABLES</Anchor>\n"
5283 "<Name>Variables</Name>\n"
5284 "<Subnodes>\n", xmlid ++);
5285 else
5286 fprintf(out, "<li><a href=\"%s#VARIABLES\"%s>Variables</a>"
5287 "<ul class=\"code\">\n", target ? target : "", targetattr);
5288
5289 while (arg)
5290 {
5291 name = mxmlElementGetAttr(arg, "name");
5292 description = mxmlFindElement(arg, arg, "description",
5293 NULL, NULL, MXML_DESCEND_FIRST);
5294
5295 if (xml)
5296 {
5297 fprintf(out, "<Node id=\"%d\">\n"
5298 "<Path>Documentation/index.html</Path>\n"
5299 "<Anchor>%s</Anchor>\n"
5300 "<Name>%s</Name>\n"
5301 "</Node>\n", xmlid ++, name, name);
5302 }
5303 else
5304 {
5305 fprintf(out, "\t<li><a href=\"%s#%s\"%s title=\"",
5306 target ? target : "", name, targetattr);
5307 write_description(out, description, "", 1);
5308 fprintf(out, "\">%s</a></li>\n", name);
5309 }
5310
5311 arg = find_public(arg, doc, "variable");
5312 }
5313
5314 if (xml)
5315 fputs("</Subnodes></Node>\n", out);
5316 else
5317 fputs("</ul></li>\n", out);
5318 }
5319
5320 /*
5321 * Enumerations/constants...
5322 */
5323
5324 if ((scut = find_public(doc, doc, "enumeration")) != NULL)
5325 {
5326 if (xml)
5327 fprintf(out, "<Node id=\"%d\">\n"
5328 "<Path>Documentation/index.html</Path>\n"
5329 "<Anchor>ENUMERATIONS</Anchor>\n"
5330 "<Name>Constants</Name>\n"
5331 "<Subnodes>\n", xmlid ++);
5332 else
5333 fprintf(out, "<li><a href=\"%s#ENUMERATIONS\"%s>Constants</a>"
5334 "<ul class=\"code\">\n", target ? target : "", targetattr);
5335
5336 while (scut)
5337 {
5338 name = mxmlElementGetAttr(scut, "name");
5339 description = mxmlFindElement(scut, scut, "description",
5340 NULL, NULL, MXML_DESCEND_FIRST);
5341
5342 if (xml)
5343 {
5344 fprintf(out, "<Node id=\"%d\">\n"
5345 "<Path>Documentation/index.html</Path>\n"
5346 "<Anchor>%s</Anchor>\n"
5347 "<Name>%s</Name>\n"
5348 "</Node>\n", xmlid ++, name, name);
5349 }
5350 else
5351 {
5352 fprintf(out, "\t<li><a href=\"%s#%s\"%s title=\"",
5353 target ? target : "", name, targetattr);
5354 write_description(out, description, "", 1);
5355 fprintf(out, "\">%s</a></li>\n", name);
5356 }
5357
5358 scut = find_public(scut, doc, "enumeration");
5359 }
5360
5361 if (xml)
5362 fputs("</Subnodes></Node>\n", out);
5363 else
5364 fputs("</ul></li>\n", out);
5365 }
5366
5367 /*
5368 * Close out the HTML table-of-contents list as needed...
5369 */
5370
5371 if (!xml)
5372 fputs("</ul>\n", out);
5373}
5374
5375
5376/*
5377 * 'write_tokens()' - Write <Token> nodes for all APIs.
5378 */
5379
5380static void
5381write_tokens(FILE *out, /* I - Output file */
5382 mxml_node_t *doc, /* I - Document */
5383 const char *path) /* I - Path to help file */
5384{
5385 mxml_node_t *function, /* Current function */
5386 *scut, /* Struct/class/union/typedef */
5387 *arg, /* Current argument */
5388 *description, /* Description of function/var */
5389 *type, /* Type node */
5390 *node; /* Current child node */
5391 const char *name, /* Name of function/type */
5392 *cename, /* Current class/enum name */
5393 *defval; /* Default value for argument */
5394 char prefix; /* Prefix for declarations */
5395
5396
5397 /*
5398 * Classes...
5399 */
5400
5401 if ((scut = find_public(doc, doc, "class")) != NULL)
5402 {
5403 while (scut)
5404 {
5405 cename = mxmlElementGetAttr(scut, "name");
5406 description = mxmlFindElement(scut, scut, "description",
5407 NULL, NULL, MXML_DESCEND_FIRST);
5408
5409 fprintf(out, "<Token>\n"
5410 "<Path>Documentation/%s</Path>\n"
5411 "<Anchor>%s</Anchor>\n"
5412 "<TokenIdentifier>//apple_ref/cpp/cl/%s</TokenIdentifier>\n"
5413 "<Abstract>", path, cename, cename);
5414 write_description(out, description, "", 1);
5415 fputs("</Abstract>\n"
5416 "</Token>\n", out);
5417
5418 if ((function = find_public(scut, scut, "function")) != NULL)
5419 {
5420 while (function)
5421 {
5422 name = mxmlElementGetAttr(function, "name");
5423 description = mxmlFindElement(function, function, "description",
5424 NULL, NULL, MXML_DESCEND_FIRST);
5425
5426 fprintf(out, "<Token>\n"
5427 "<Path>Documentation/%s</Path>\n"
5428 "<Anchor>%s.%s</Anchor>\n"
5429 "<TokenIdentifier>//apple_ref/cpp/clm/%s/%s", path,
5430 cename, name, cename, name);
5431
5432 arg = mxmlFindElement(function, function, "returnvalue", NULL,
5433 NULL, MXML_DESCEND_FIRST);
5434
5435 if (arg && (type = mxmlFindElement(arg, arg, "type", NULL,
5436 NULL, MXML_DESCEND_FIRST)) != NULL)
5437 {
5438 for (node = type->child; node; node = node->next)
5439 fputs(node->value.text.string, out);
5440 }
5441 else if (strcmp(cename, name) && strcmp(cename, name + 1))
5442 fputs("void", out);
5443
5444 fputs("/", out);
5445
5446 for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
5447 MXML_DESCEND_FIRST), prefix = '(';
5448 arg;
5449 arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
5450 MXML_NO_DESCEND), prefix = ',')
5451 {
5452 type = mxmlFindElement(arg, arg, "type", NULL, NULL,
5453 MXML_DESCEND_FIRST);
5454
5455 putc(prefix, out);
5456
5457 for (node = type->child; node; node = node->next)
5458 fputs(node->value.text.string, out);
5459
5460 fputs(mxmlElementGetAttr(arg, "name"), out);
5461 }
5462
5463 if (prefix == '(')
5464 fputs("(void", out);
5465
5466 fputs(")</TokenIdentifier>\n"
5467 "<Abstract>", out);
5468 write_description(out, description, "", 1);
5469 fputs("</Abstract>\n"
5470 "<Declaration>", out);
5471
5472 arg = mxmlFindElement(function, function, "returnvalue", NULL,
5473 NULL, MXML_DESCEND_FIRST);
5474
5475 if (arg)
5476 write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL,
5477 NULL, MXML_DESCEND_FIRST),
5478 OUTPUT_XML);
5479 else if (strcmp(cename, name) && strcmp(cename, name + 1))
5480 fputs("void ", out);
5481
5482 fputs(name, out);
5483
5484 for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
5485 MXML_DESCEND_FIRST), prefix = '(';
5486 arg;
5487 arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
5488 MXML_NO_DESCEND), prefix = ',')
5489 {
5490 type = mxmlFindElement(arg, arg, "type", NULL, NULL,
5491 MXML_DESCEND_FIRST);
5492
5493 putc(prefix, out);
5494 if (prefix == ',')
5495 putc(' ', out);
5496
5497 if (type->child)
5498 write_element(out, doc, type, OUTPUT_XML);
5499
5500 fputs(mxmlElementGetAttr(arg, "name"), out);
5501 if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
5502 fprintf(out, " %s", defval);
5503 }
5504
5505 if (prefix == '(')
5506 fputs("(void);", out);
5507 else
5508 fputs(");", out);
5509
5510 fputs("</Declaration>\n"
5511 "</Token>\n", out);
5512
5513 function = find_public(function, doc, "function");
5514 }
5515 }
5516 scut = find_public(scut, doc, "class");
5517 }
5518 }
5519
5520 /*
5521 * Functions...
5522 */
5523
5524 if ((function = find_public(doc, doc, "function")) != NULL)
5525 {
5526 while (function)
5527 {
5528 name = mxmlElementGetAttr(function, "name");
5529 description = mxmlFindElement(function, function, "description",
5530 NULL, NULL, MXML_DESCEND_FIRST);
5531
5532 fprintf(out, "<Token>\n"
5533 "<Path>Documentation/%s</Path>\n"
5534 "<Anchor>%s</Anchor>\n"
5535 "<TokenIdentifier>//apple_ref/c/func/%s</TokenIdentifier>\n"
5536 "<Abstract>", path, name, name);
5537 write_description(out, description, "", 1);
5538 fputs("</Abstract>\n"
5539 "<Declaration>", out);
5540
5541 arg = mxmlFindElement(function, function, "returnvalue", NULL,
5542 NULL, MXML_DESCEND_FIRST);
5543
5544 if (arg)
5545 write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL,
5546 NULL, MXML_DESCEND_FIRST),
5547 OUTPUT_XML);
5548 else // if (strcmp(cname, name) && strcmp(cname, name + 1))
5549 fputs("void ", out);
5550
5551 fputs(name, out);
5552
5553 for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
5554 MXML_DESCEND_FIRST), prefix = '(';
5555 arg;
5556 arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
5557 MXML_NO_DESCEND), prefix = ',')
5558 {
5559 type = mxmlFindElement(arg, arg, "type", NULL, NULL,
5560 MXML_DESCEND_FIRST);
5561
5562 putc(prefix, out);
5563 if (prefix == ',')
5564 putc(' ', out);
5565
5566 if (type->child)
5567 write_element(out, doc, type, OUTPUT_XML);
5568
5569 fputs(mxmlElementGetAttr(arg, "name"), out);
5570 if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
5571 fprintf(out, " %s", defval);
5572 }
5573
5574 if (prefix == '(')
5575 fputs("(void);", out);
5576 else
5577 fputs(");", out);
5578
5579 fputs("</Declaration>\n"
5580 "</Token>\n", out);
5581
5582 function = find_public(function, doc, "function");
5583 }
5584 }
5585
5586 /*
5587 * Data types...
5588 */
5589
5590 if ((scut = find_public(doc, doc, "typedef")) != NULL)
5591 {
5592 while (scut)
5593 {
5594 name = mxmlElementGetAttr(scut, "name");
5595 description = mxmlFindElement(scut, scut, "description",
5596 NULL, NULL, MXML_DESCEND_FIRST);
5597
5598 fprintf(out, "<Token>\n"
5599 "<Path>Documentation/%s</Path>\n"
5600 "<Anchor>%s</Anchor>\n"
5601 "<TokenIdentifier>//apple_ref/c/tdef/%s</TokenIdentifier>\n"
5602 "<Abstract>", path, name, name);
5603 write_description(out, description, "", 1);
5604 fputs("</Abstract>\n"
5605 "</Token>\n", out);
5606
5607 scut = find_public(scut, doc, "typedef");
5608 }
5609 }
5610
5611 /*
5612 * Structures...
5613 */
5614
5615 if ((scut = find_public(doc, doc, "struct")) != NULL)
5616 {
5617 while (scut)
5618 {
5619 name = mxmlElementGetAttr(scut, "name");
5620 description = mxmlFindElement(scut, scut, "description",
5621 NULL, NULL, MXML_DESCEND_FIRST);
5622
5623 fprintf(out, "<Token>\n"
5624 "<Path>Documentation/%s</Path>\n"
5625 "<Anchor>%s</Anchor>\n"
5626 "<TokenIdentifier>//apple_ref/c/tag/%s</TokenIdentifier>\n"
5627 "<Abstract>", path, name, name);
5628 write_description(out, description, "", 1);
5629 fputs("</Abstract>\n"
5630 "</Token>\n", out);
5631
5632 scut = find_public(scut, doc, "struct");
5633 }
5634 }
5635
5636 /*
5637 * Unions...
5638 */
5639
5640 if ((scut = find_public(doc, doc, "union")) != NULL)
5641 {
5642 while (scut)
5643 {
5644 name = mxmlElementGetAttr(scut, "name");
5645 description = mxmlFindElement(scut, scut, "description",
5646 NULL, NULL, MXML_DESCEND_FIRST);
5647
5648 fprintf(out, "<Token>\n"
5649 "<Path>Documentation/%s</Path>\n"
5650 "<Anchor>%s</Anchor>\n"
5651 "<TokenIdentifier>//apple_ref/c/tag/%s</TokenIdentifier>\n"
5652 "<Abstract>", path, name, name);
5653 write_description(out, description, "", 1);
5654 fputs("</Abstract>\n"
5655 "</Token>\n", out);
5656
5657 scut = find_public(scut, doc, "union");
5658 }
5659 }
5660
5661 /*
5662 * Globals variables...
5663 */
5664
5665 if ((arg = find_public(doc, doc, "variable")) != NULL)
5666 {
5667 while (arg)
5668 {
5669 name = mxmlElementGetAttr(arg, "name");
5670 description = mxmlFindElement(arg, arg, "description",
5671 NULL, NULL, MXML_DESCEND_FIRST);
5672
5673 fprintf(out, "<Token>\n"
5674 "<Path>Documentation/%s</Path>\n"
5675 "<Anchor>%s</Anchor>\n"
5676 "<TokenIdentifier>//apple_ref/c/data/%s</TokenIdentifier>\n"
5677 "<Abstract>", path, name, name);
5678 write_description(out, description, "", 1);
5679 fputs("</Abstract>\n"
5680 "</Token>\n", out);
5681
5682 arg = find_public(arg, doc, "variable");
5683 }
5684 }
5685
5686 /*
5687 * Enumerations/constants...
5688 */
5689
5690 if ((scut = find_public(doc, doc, "enumeration")) != NULL)
5691 {
5692 while (scut)
5693 {
5694 cename = mxmlElementGetAttr(scut, "name");
5695 description = mxmlFindElement(scut, scut, "description",
5696 NULL, NULL, MXML_DESCEND_FIRST);
5697
5698 fprintf(out, "<Token>\n"
5699 "<Path>Documentation/%s</Path>\n"
5700 "<Anchor>%s</Anchor>\n"
5701 "<TokenIdentifier>//apple_ref/c/tag/%s</TokenIdentifier>\n"
5702 "<Abstract>", path, cename, cename);
5703 write_description(out, description, "", 1);
5704 fputs("</Abstract>\n"
5705 "</Token>\n", out);
5706
5707 for (arg = mxmlFindElement(scut, scut, "constant", NULL, NULL,
5708 MXML_DESCEND_FIRST);
5709 arg;
5710 arg = mxmlFindElement(arg, scut, "constant", NULL, NULL,
5711 MXML_NO_DESCEND))
5712 {
5713 name = mxmlElementGetAttr(arg, "name");
5714 description = mxmlFindElement(arg, arg, "description", NULL,
5715 NULL, MXML_DESCEND_FIRST);
5716 fprintf(out, "<Token>\n"
5717 "<Path>Documentation/%s</Path>\n"
5718 "<Anchor>%s</Anchor>\n"
5719 "<TokenIdentifier>//apple_ref/c/econst/%s</TokenIdentifier>\n"
5720 "<Abstract>", path, cename, name);
5721 write_description(out, description, "", 1);
5722 fputs("</Abstract>\n"
5723 "</Token>\n", out);
5724 }
5725
5726 scut = find_public(scut, doc, "enumeration");
5727 }
5728 }
5729}
5730
5731
5732/*
5733 * 'ws_cb()' - Whitespace callback for saving.
5734 */
5735
5736static const char * /* O - Whitespace string or NULL for none */
5737ws_cb(mxml_node_t *node, /* I - Element node */
5738 int where) /* I - Where value */
5739{
5740 const char *name; /* Name of element */
5741 int depth; /* Depth of node */
5742 static const char *spaces = " ";
5743 /* Whitespace (40 spaces) for indent */
5744
5745
5746 name = node->value.element.name;
5747
5748 switch (where)
5749 {
5750 case MXML_WS_BEFORE_CLOSE :
5751 if (strcmp(name, "argument") &&
5752 strcmp(name, "class") &&
5753 strcmp(name, "constant") &&
5754 strcmp(name, "enumeration") &&
5755 strcmp(name, "function") &&
5756 strcmp(name, "mxmldoc") &&
5757 strcmp(name, "namespace") &&
5758 strcmp(name, "returnvalue") &&
5759 strcmp(name, "struct") &&
5760 strcmp(name, "typedef") &&
5761 strcmp(name, "union") &&
5762 strcmp(name, "variable"))
5763 return (NULL);
5764
5765 for (depth = -4; node; node = node->parent, depth += 2);
5766 if (depth > 40)
5767 return (spaces);
5768 else if (depth < 2)
5769 return (NULL);
5770 else
5771 return (spaces + 40 - depth);
5772
5773 case MXML_WS_AFTER_CLOSE :
5774 return ("\n");
5775
5776 case MXML_WS_BEFORE_OPEN :
5777 for (depth = -4; node; node = node->parent, depth += 2);
5778 if (depth > 40)
5779 return (spaces);
5780 else if (depth < 2)
5781 return (NULL);
5782 else
5783 return (spaces + 40 - depth);
5784
5785 default :
5786 case MXML_WS_AFTER_OPEN :
5787 if (strcmp(name, "argument") &&
5788 strcmp(name, "class") &&
5789 strcmp(name, "constant") &&
5790 strcmp(name, "enumeration") &&
5791 strcmp(name, "function") &&
5792 strcmp(name, "mxmldoc") &&
5793 strcmp(name, "namespace") &&
5794 strcmp(name, "returnvalue") &&
5795 strcmp(name, "struct") &&
5796 strcmp(name, "typedef") &&
5797 strcmp(name, "union") &&
5798 strcmp(name, "variable") &&
5799 strncmp(name, "?xml", 4))
5800 return (NULL);
5801 else
5802 return ("\n");
5803 }
5804}
5805
5806
5807/*
5808 * End of "$Id: mxmldoc.c 440 2011-08-11 18:51:26Z mike $".
5809 */
diff --git a/pathologist/src/pathologist/Makefile.am b/pathologist/src/pathologist/Makefile.am
new file mode 100644
index 0000000..215d892
--- /dev/null
+++ b/pathologist/src/pathologist/Makefile.am
@@ -0,0 +1,58 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = --coverage -O0
9 XLIB = -lgcov
10endif
11
12
13if !MINGW
14if HAVE_ESMTP
15if HAVE_OPENSSL
16
17bin_PROGRAMS = \
18 pathologist
19
20pathologist_SOURCES = \
21 pathologist.c \
22 getopt.c \
23 getopt_helpers.c \
24 mail_sender.c \
25 action.c \
26 xml_writer.c \
27 edb_api.c
28
29pathologist_LDADD = \
30 $(top_builddir)/src/mi/libmi.la \
31 $(top_builddir)/src/util/libpathologistutil.la \
32 -lesmtp \
33 -lsqlite3 \
34 $(XLIB) \
35 $(GN_LIBINTL)
36
37if ENABLE_TEST_RUN
38if SEASPIDER
39TESTS = $(check_PROGRAMS)
40endif
41endif
42
43check_PROGRAMS = \
44 test_pathologist
45
46test_pathologist_SOURCES = \
47 test_pathologist.c
48
49test_pathologist_LDADD = \
50 $(top_builddir)/src/minixml/libminixml.la
51
52EXTRA_DIST = \
53 ../../refs
54
55
56endif
57endif
58endif
diff --git a/pathologist/src/pathologist/action.c b/pathologist/src/pathologist/action.c
new file mode 100644
index 0000000..dcd1872
--- /dev/null
+++ b/pathologist/src/pathologist/action.c
@@ -0,0 +1,1124 @@
1
2#include "monkey_common.h"
3#include "action.h"
4#include "pathologist_edb.h"
5#include "pathologist_xml_writer.h"
6#include <libesmtp.h>
7#include <string.h>
8#include <stdio.h>
9#include <sys/stat.h>
10#include <errno.h>
11
12extern void sendMail(const char *messageContents, const char *reportFileName,
13 const char *emailAddress);
14
15static int crashExpressionFoundInEDB = MONKEY_YES;
16static int async_c = 0;
17static int stoppedInSharedLib = MONKEY_NO;
18static int failureFunctionStartLine = 0; // start line number of the function in which the failure occurs
19static struct WatchInfo *watchInfoListHead = NULL;
20static struct WatchInfo *watchInfoListTail = NULL;
21static struct FileName *fileNameListHead = NULL;
22static struct FileName *fileNameListTail = NULL;
23static struct ScopeEnd *scopeEndListHead = NULL;
24static struct ScopeEnd *scopeEndListTail = NULL;
25
26
27struct ScopeEnd {
28 struct ScopeEnd *next;
29 struct ScopeEnd *prev;
30
31 int lineNo;
32};
33
34
35struct FileName {
36 struct FileName *next;
37 struct FileName *prev;
38
39 const char *name;
40};
41
42
43struct Expression {
44 struct Expression *next;
45 struct Expression *prev;
46 const char *expressionSyntax;
47 const char *expressionValue;
48 int isCall;
49};
50
51
52struct Function {
53 struct Function *next;
54 struct Function *prev;
55 const char *name;
56 const char *file;
57 int line;
58 int depth;
59 struct Expression *expressionListHead;
60 struct Expression *expressionListTail;
61};
62
63struct Trace {
64 struct Trace *next;
65 struct Trace *prev;
66 struct Function *functionListHead;
67 struct Function *functionListTail;
68 struct Expression *globalsListHead;
69 struct Expression *globalsListTail;
70};
71
72
73struct Epoch {
74 struct Trace *traceListHead;
75 struct Trace *traceListTail;
76} *epoch;
77
78struct WatchInfo {
79 struct WatchInfo *next;
80 struct WatchInfo *prev;
81 int hitNumber;
82 const char *value;
83};
84
85
86static void cb_console(const char *str, void *data)
87{
88 printf("CONSOLE> %s\n", str);
89}
90
91
92/* Note that unlike what's documented in gdb docs it isn't usable. */
93static void cb_target(const char *str, void *data)
94{
95 printf("TARGET> %s\n", str);
96}
97
98
99static void cb_log(const char *str, void *data)
100{
101 printf("LOG> %s\n", str);
102}
103
104
105static void cb_to(const char *str, void *data)
106{
107 printf(">> %s", str);
108}
109
110
111static void cb_from(const char *str, void *data)
112{
113 printf("<< %s\n", str);
114}
115
116
117static void cb_async(mi_output * o, void *data)
118{
119 printf("ASYNC\n");
120 async_c++;
121}
122
123static int isInCodeBase(const char *name)
124{
125 struct FileName *fileName = fileNameListHead;
126 while (fileName) {
127 if (strstr(name, fileName->name))
128 return MONKEY_YES;
129 fileName = fileName->next;
130 }
131 return MONKEY_NO;
132}
133
134
135static int wait_for_stop(struct MONKEY_ACTION_Context *cntxt)
136{
137 static int safetyCount = 0;
138
139 while (!mi_get_response(cntxt->gdb_handle))
140 usleep(GDB_MI_ASYNC_WAIT);
141 /* The end of the async. */
142
143 cntxt->gdb_stop_reason = mi_res_stop(cntxt->gdb_handle);
144
145 if (cntxt->gdb_stop_reason) {
146 safetyCount = 0; // reset the Safety Count whenever you stop for a meaningful reason
147
148 switch (cntxt->gdb_stop_reason->reason) {
149 case sr_exited_normally:
150 return GDB_STATE_EXIT_NORMALLY;
151 case sr_signal_received:
152 //gmi_set_unwind_on_signal_on(cntxt->gdb_handle); // Program crashed. From now on preserve stack on any further received signals
153 break;
154 case sr_bkpt_hit:
155 {
156 /* continue execution */
157 gmi_exec_continue(cntxt->gdb_handle);
158 return wait_for_stop(cntxt);
159 }
160 case sr_wp_trigger:
161 {
162 /* Execution stopped because of hitting a watch point */
163 static int watchPointHitNumber = 0;
164 struct WatchInfo *watchInfo =
165 MONKEY_malloc(sizeof(struct WatchInfo));
166 watchInfo->hitNumber = ++watchPointHitNumber;
167 watchInfo->value = cntxt->gdb_stop_reason->wp_val;
168 MONKEY_CONTAINER_DLL_insert(watchInfoListHead,
169 watchInfoListTail, watchInfo);
170 if (watchPointHitNumber == 1023)
171 printf("HEY! 1023! WE ARE GETTING OUT OF THE LOOP!\n");
172 gmi_exec_continue(cntxt->gdb_handle);
173 return wait_for_stop(cntxt);
174 }
175 case sr_wp_scope:
176 gmi_exec_continue(cntxt->gdb_handle);
177 return wait_for_stop(cntxt);
178 default:
179 break;
180 }
181
182 /* Reaching this line means that the program has stopped abnormally
183 * OR we are in backtracking mode, and it's a step backward */
184 cntxt->gdb_frames = gmi_stack_list_frames(cntxt->gdb_handle);
185 while(cntxt->gdb_frames && !cntxt->gdb_frames->file) {
186 cntxt->gdb_frames = cntxt->gdb_frames->next;
187 }
188
189 if(cntxt->gdb_frames && !isInCodeBase(cntxt->gdb_frames->file)) {
190 struct FileName* filen;
191 for(filen=fileNameListHead; filen; filen = filen->next)
192 if(filen->name) printf("%s\n", filen->name);
193 while(cntxt->gdb_frames) {
194 if(cntxt->gdb_frames->file)
195 printf("file: %s\nisInCodeBase: %d\n", cntxt->gdb_frames->file, isInCodeBase(cntxt->gdb_frames->file));
196 if(cntxt->gdb_frames->file && isInCodeBase(cntxt->gdb_frames->file)) {
197 break;
198 }
199 cntxt->gdb_frames = cntxt->gdb_frames->next;
200 }
201
202 if (cntxt->gdb_frames && cntxt->run_reverse) {
203 /* We are in backtracking mode, we need to move GDB from the shared library to our source code */
204 stoppedInSharedLib = MONKEY_YES;
205 gmi_break_insert(cntxt->gdb_handle, cntxt->gdb_frames->file,
206 cntxt->gdb_frames->line);
207 gmi_exec_continue_reverse(cntxt->gdb_handle);
208 while (!mi_get_response(cntxt->gdb_handle))
209 usleep(GDB_MI_ASYNC_WAIT);
210 cntxt->gdb_frames = gmi_stack_list_frames(cntxt->gdb_handle);
211 }
212 }
213
214 if (NULL == cntxt->gdb_frames) {
215 fprintf(stderr, "Cannot get stack frames from gdb. You might be debugging the wrong program.\n"); // Something really bad happened. Abort!
216 return GDB_STATE_ERROR;
217 }
218
219 /* Change current GDB frame to the one containing source code */
220 gmi_stack_select_frame(cntxt->gdb_handle,
221 cntxt->gdb_frames->level);
222
223 return GDB_STATE_STOPPED;
224 }
225 else if ((MONKEY_YES == cntxt->run_reverse) && (safetyCount < GDB_SAFETY_COUNT)) { // GDB_SAFETY_COUNT prevents infinite analysis
226 /* If the stop reason is NULL, don't bail out immediately, wait for stop again.
227 * It happens when a breakpoint is placed (for backtracking) that the program stops for an unknown reason,
228 * resulting in a NULL stop reason which causes Monkey to bail out before finishing its analysis. This else if
229 * clause prevents premature bail out
230 */
231 safetyCount++;
232 return wait_for_stop(cntxt);
233 }
234
235 return GDB_STATE_ERROR;
236}
237
238
239int
240MONKEY_ACTION_report_file(struct MONKEY_ACTION_Context *cntxt,
241 const char *dumpFileName, int isXML)
242{
243 FILE *f;
244 f = MONKEY_XML_WRITER_create_document(dumpFileName);
245 if (NULL == f) {
246 fprintf(stderr, "Error: pathologist was not able to create report file.\n");
247 return MONKEY_NO;
248 }
249 MONKEY_XML_WRITER_write_document(f, cntxt->xml_report_node);
250 if (0 != fclose(f)) {
251 fprintf(stderr, "Error: pathologist could not close report file.\n");
252 return MONKEY_NO;
253 }
254 return MONKEY_OK;
255}
256
257
258int MONKEY_ACTION_report_email(struct MONKEY_ACTION_Context *cntxt, const char * dumpFileName)
259{
260 FILE *f;
261 MONKEY_ACTION_format_report_xml(cntxt);
262 f = MONKEY_XML_WRITER_create_document(dumpFileName);
263 MONKEY_XML_WRITER_write_document(f, cntxt->xml_report_node);
264 fclose(f);
265 sendMail(NULL, dumpFileName, cntxt->email_address);
266 return MONKEY_OK;
267}
268
269
270static int
271iterateFileNames(void *cls, int numColumns, char **colValues,
272 char **colNames)
273{
274 struct FileName *fileName;
275 char *token;
276
277 if (NULL == colValues[0])
278 return 1; /* Error */
279
280 // This is done to extract the only the file name from the full path coming from the Database
281 token = strrchr(colValues[0], '/');
282 if (NULL == token) {
283 token = colValues[0];
284 } else {
285 token++;
286 }
287 fileName = MONKEY_malloc(sizeof(struct FileName));
288 fileName->prev = NULL;
289 fileName->next = NULL;
290 fileName->name = MONKEY_strdup(token);
291 MONKEY_CONTAINER_DLL_insert(fileNameListHead, fileNameListTail, fileName);
292 return 0; /* OK */
293}
294
295
296static int
297outerScopesCallback(void *cls, int numColumns, char **colValues,
298 char **colNames)
299{
300 if (NULL == colValues[0])
301 return 1; /* Error */
302
303 struct ScopeEnd *scopeEnd = MONKEY_malloc(sizeof(struct ScopeEnd));
304 scopeEnd->lineNo = atoi(colValues[0]);
305 MONKEY_CONTAINER_DLL_insert(scopeEndListHead, scopeEndListTail,
306 scopeEnd);
307 return 0; /* OK */
308}
309
310
311static int
312iterateExpressions(void *cls, int numColumns, char **colValues,
313 char **colNames)
314{
315 struct Expression *expression;
316 struct Function *function = (struct Function *) cls;
317
318 if (NULL == colValues[0] || NULL == colValues[1])
319 return 1; /* Error */
320
321 expression = MONKEY_malloc(sizeof(struct Expression));
322 expression->expressionSyntax = MONKEY_strdup(colValues[0]);
323 expression->isCall = atoi(colValues[1]);
324 expression->expressionValue = NULL;
325 expression->next = NULL;
326 expression->prev = NULL;
327
328 MONKEY_CONTAINER_DLL_insert(function->expressionListHead,
329 function->expressionListTail, expression);
330
331 return 0; /* OK */
332}
333
334
335static int
336iterateGlobals(void *cls, int numColumns, char **colValues,
337 char **colNames)
338{
339 struct Expression *expression;
340 struct Trace *trace = (struct Trace *) cls;
341
342 if (NULL == colValues[0] || NULL == colValues[1])
343 return 1; /* Error */
344
345 expression = MONKEY_malloc(sizeof(struct Expression));
346 expression->expressionSyntax = MONKEY_strdup(colValues[0]);
347 expression->isCall = atoi(colValues[1]);
348 expression->expressionValue = NULL;
349 expression->next = NULL;
350 expression->prev = NULL;
351
352 MONKEY_CONTAINER_DLL_insert(trace->globalsListHead,
353 trace->globalsListTail, expression);
354
355 return 0; /* OK */
356}
357
358
359static int
360scopeEndCallback(void *cls, int numColumns, char **colValues,
361 char **colNames)
362{
363 int *scopeEnd = (int *) cls;
364
365 *scopeEnd = atoi(colValues[0]);
366 if (*scopeEnd < 0)
367 return 1; /* Error */
368 return 0;
369}
370
371
372static int isAssignment(const char *expressionSyntax)
373{
374 return (NULL != strstr(expressionSyntax, "=")) ? MONKEY_YES : MONKEY_NO;
375}
376
377
378static struct Expression*
379analyzeExpressionValue( struct MONKEY_ACTION_Context *cntxt,
380 struct Expression *expr,
381 struct Expression **expressionListHead,
382 struct Expression **expressionListTail)
383{
384 MONKEY_assert(expr);
385 struct Expression *next = expr->next;
386 struct Expression *removedExpression = expr;
387
388 // We will not evaluate function calls (because GDB will evaluate by calling the function)
389 if( expr->isCall )
390 goto remove;
391
392 // We should NOT evaluate assignments, otherwise subsequent expression evaluations will be spoiled
393 else if( isAssignment(expr->expressionSyntax) )
394 goto remove;
395
396 // NULL Pointer are also ignored (cause theire NULL)
397 if (strcmp(expr->expressionSyntax, "NULL") == 0 ||
398 strcmp(expr->expressionSyntax, "0x0") == 0 ||
399 strcmp(expr->expressionSyntax, "((void*)0)") == 0)
400 goto remove;
401
402 expr->expressionValue =
403 gmi_data_evaluate_expression( cntxt->gdb_handle,
404 expr->expressionSyntax);
405
406 // ignore trivial report entries (constants for that matter)
407 if (expr->expressionValue && strcmp(expr->expressionSyntax, expr->expressionValue) == 0)
408 goto remove;
409
410 // ignore functionpointers with their default names
411 size_t value_len; //= strlen(expr->expressionValue);
412 size_t syntax_len; //= strlen(expr->expressionSyntax);
413 if (expr->expressionValue &&
414 (value_len = strlen(expr->expressionValue)) > (syntax_len = strlen(expr->expressionSyntax)) + 2 &&
415 !strncmp(expr->expressionValue + value_len - 1, ">", 1) &&
416 !strncmp(expr->expressionValue + value_len - syntax_len - 2, "<", 1) &&
417 !strncmp(expr->expressionValue + value_len - syntax_len - 1, expr->expressionSyntax, syntax_len))
418 goto remove;
419
420 if (NULL != expr->expressionValue
421 && (strcmp(expr->expressionValue, "0x0") == 0
422 || strcmp(expr->expressionValue, "NULL") == 0))
423 {
424 expr->expressionValue = "NULL";
425 cntxt->gdb_null_variable = expr->expressionSyntax;
426 cntxt->has_null = MONKEY_YES;
427 }
428
429 return next;
430
431remove:
432 MONKEY_CONTAINER_DLL_remove(*expressionListHead,
433 *expressionListTail,
434 removedExpression);
435 return next;
436}
437
438
439static int
440analyzeExpressionValues(struct Function *function,
441 struct MONKEY_ACTION_Context *cntxt)
442{
443 struct Expression *tmp;
444
445 tmp = function->expressionListHead;
446 while (NULL != tmp) {
447 tmp = analyzeExpressionValue(cntxt, tmp, &(function->expressionListHead), &(function->expressionListTail));
448 }
449
450 return MONKEY_OK;
451}
452
453
454static int
455analyzeGlobalsValues(struct Trace *trace,
456 struct MONKEY_ACTION_Context *cntxt)
457{
458 struct Expression *tmp;
459
460 tmp = trace->globalsListHead;
461 while (NULL != tmp) {
462 tmp = analyzeExpressionValue(cntxt, tmp, &(trace->globalsListHead), &(trace->globalsListTail));
463 }
464
465 return MONKEY_OK;
466}
467
468
469static int flushTrace(struct Trace *trace)
470{
471 struct Function *functionPtr = NULL;
472 struct Expression *expressionPtr = NULL;
473
474 while (NULL != trace->functionListHead) {
475 functionPtr = trace->functionListHead;
476 trace->functionListHead = trace->functionListHead->next;
477
478 while (NULL != functionPtr->expressionListHead) {
479 expressionPtr = functionPtr->expressionListHead;
480 functionPtr->expressionListHead =
481 functionPtr->expressionListHead->next;
482
483 MONKEY_free((char *) expressionPtr->expressionSyntax);
484 MONKEY_free(expressionPtr);
485 }
486 MONKEY_free(functionPtr);
487 }
488 return MONKEY_OK;
489}
490
491static void printTrace()
492{
493 int i = 0;
494 struct Function *functionPtr;
495 struct Expression *expressionPtr;
496 struct Trace *tracePtr = epoch->traceListHead;
497 while (NULL != tracePtr) {
498 printf("Program Stack Trace: Epoch Step %d\n", i++);
499 functionPtr = tracePtr->functionListHead;
500 while (NULL != functionPtr) {
501 printf("Function: %s, file: %s, line: %d\n", functionPtr->name,
502 functionPtr->file, functionPtr->line);
503 printf
504 ("--------------------------------------------------------------------------------------------------\n");
505 printf("Expressions:\n");
506
507 expressionPtr = functionPtr->expressionListHead;
508 while (NULL != expressionPtr) {
509 printf("%s = %s\n",
510 expressionPtr->expressionSyntax,
511 (NULL ==
512 expressionPtr->
513 expressionValue) ? "No Value" : expressionPtr->
514 expressionValue);
515 expressionPtr = expressionPtr->next;
516 }
517
518 functionPtr = functionPtr->next;
519 }
520 printf
521 ("############################## End Stack Trace ######################################################\n\n");
522 tracePtr = tracePtr->next;
523 }
524}
525
526static void flushScopeEndList()
527{
528 struct ScopeEnd* scopeEndPtr;
529 while (NULL != scopeEndListHead) {
530 scopeEndPtr = scopeEndListHead;
531 scopeEndListHead = scopeEndPtr->next;
532 MONKEY_free(scopeEndPtr);
533 }
534
535 scopeEndListHead = NULL;
536 scopeEndListTail = NULL;
537}
538
539int MONKEY_ACTION_inspect_expression_database(struct MONKEY_ACTION_Context *cntxt)
540{
541 int ret = MONKEY_OK;
542 int stackDepth = 0;
543 char* file_names = NULL;
544 struct Function *function = NULL;
545 struct Trace *trace = NULL;
546
547 /* Variables used across recursive calls */
548 static struct MONKEY_EDB_Context *edbCntxt;
549
550 if (NULL == epoch) {
551 epoch = MONKEY_malloc(sizeof(struct Epoch));
552 epoch->traceListHead = NULL;
553 epoch->traceListTail = NULL;
554 }
555
556 // Initialize the trace structure for this particular epoch step
557 trace = MONKEY_malloc(sizeof(struct Trace));
558 trace->globalsListHead = NULL;
559 trace->globalsListTail = NULL;
560 MONKEY_CONTAINER_DLL_insert_tail(epoch->traceListHead,
561 epoch->traceListTail, trace);
562
563
564 // Create a connection to the Expression Database
565 edbCntxt = MONKEY_EDB_connect(cntxt->expression_database_path);
566 if (NULL == edbCntxt) {
567 fprintf(stderr, "Unable to connect to Expression Database file!\n");
568 ret = MONKEY_NO;
569 goto cleanup;
570 }
571
572 while( cntxt->gdb_frames && stackDepth <= cntxt->scope_depth) {
573 gmi_stack_select_frame(cntxt->gdb_handle, cntxt->gdb_frames->level);
574 gmi_stack_info_frame(cntxt->gdb_handle);
575
576 // build the function struct
577 function = MONKEY_malloc(sizeof(struct Function));
578 function->depth = stackDepth++;
579 function->line = cntxt->gdb_frames->line;
580 function->name = cntxt->gdb_frames->func;
581 function->file = cntxt->gdb_frames->file;
582 function->expressionListHead = NULL;
583 function->expressionListTail = NULL;
584 function->next = NULL;
585 function->prev = NULL;
586 MONKEY_CONTAINER_DLL_insert_tail(trace->functionListHead,
587 trace->functionListTail, function);
588
589 // Retrieve scope expressions from the database
590 MONKEY_EDB_get_expressions(edbCntxt,
591 cntxt->gdb_frames->file,
592 cntxt->gdb_frames->line,
593 &iterateExpressions, function);
594
595 // Do value analysis for relevant expressions
596 analyzeExpressionValues(function, cntxt);
597
598 // add current filename to the list
599 char* tmp = file_names;
600 MONKEY_asprintf(&file_names,
601 "%s OR file_name LIKE \'%%%s\'",
602 file_names,
603 cntxt->gdb_frames->file);
604 MONKEY_free_non_null(tmp);
605
606 /* Now, dive deeper into the stack trace */
607 cntxt->gdb_frames = cntxt->gdb_frames->next;
608 }
609
610
611 // Retrieve globals from the database
612 file_names += 10; // cut the first 10 chars "(null) OR "
613 MONKEY_EDB_get_globals( edbCntxt,
614 file_names,
615 &iterateGlobals, trace);
616 file_names -= 10;
617
618 // Do value analysis for globals
619 analyzeGlobalsValues(trace, cntxt);
620
621cleanup:
622 MONKEY_EDB_disconnect(edbCntxt);
623 MONKEY_free_non_null(file_names);
624 return ret;
625}
626
627
628int MONKEY_ACTION_start_reverse_execution(struct MONKEY_ACTION_Context
629 *cntxt)
630{
631 const char *failureFile = epoch->traceListHead->functionListHead->file;
632 const char *failureFunction =
633 epoch->traceListHead->functionListHead->name;
634 struct Trace *tracePtr = epoch->traceListHead;
635 int oldLine;
636
637 /*
638 * We execute one step back operation before starting analysis. We do that to go to the expression before the one where the crash occurred.
639 * Because we already have the expression where the crash happened in our stack structure from the previous epoch step (the initial epoch)
640 */
641 if (MONKEY_NO == stoppedInSharedLib) {
642 if (MONKEY_NO != gmi_exec_next_reverse(cntxt->gdb_handle)) {
643 while (!mi_get_response(cntxt->gdb_handle))
644 usleep(GDB_MI_ASYNC_WAIT);
645 } else {
646 fprintf(stderr, "Error: Reverse Execution has failed! Backtracking might not be supported for the current architecture.\n");
647 if (MONKEY_YES == cntxt->gdb_connected) {
648 mi_disconnect(cntxt->gdb_handle); // No need for gdb mi at this point!
649 cntxt->gdb_connected = MONKEY_NO;
650 }
651 return MONKEY_NO;
652 }
653 }
654
655 /* Do reverse execution for all expressions in the function in which the failure occurred */
656 do {
657 oldLine = tracePtr->functionListHead->line;
658 if (MONKEY_NO != gmi_exec_next_reverse(cntxt->gdb_handle)) {
659 if (GDB_STATE_ERROR == wait_for_stop(cntxt)) {
660 fprintf(stderr, "Error: Analysis during Reverse Execution has failed!\n");
661 return MONKEY_NO;
662 }
663 if (oldLine == cntxt->gdb_frames->line)
664 break;// We reached the end of record history TODO: DIRTY SOLUTION! WILL FAIL IF TWO EXPRESSIONS ON THE SAME LINE
665 MONKEY_ACTION_inspect_expression_database(cntxt);
666 } else {
667 fprintf(stderr, "Error: Reverse Execution has failed!\n");
668 return MONKEY_NO;
669 }
670
671 tracePtr = tracePtr->next; // tracePtr will have the new trace added after examining the expression database for this reverse execution step
672 } while ((NULL != tracePtr)
673 && (strcmp(failureFile, tracePtr->functionListHead->file) ==
674 0)
675 && (strcmp(failureFunction, tracePtr->functionListHead->name)
676 == 0));
677
678 // stop recording and return
679 gmi_exec_record_stop(cntxt->gdb_handle);
680 return MONKEY_OK;
681}
682
683int MONKEY_ACTION_rerun_with_valgrind(struct MONKEY_ACTION_Context
684 *cntxt)
685{
686 char *valgrindCommand;
687 FILE *valgrindPipe;
688 const char *valgrindPath = cntxt->valgrind_binary_path;
689 struct stat buf;
690
691 if (NULL == valgrindPath)
692 valgrindPath = "/usr/bin/valgrind"; /* Assumption for valgrind installation */
693
694 /* Check if Valgrind is installed */
695 if (stat(valgrindPath, &buf) != 0 && errno == ENOENT ) {
696 fprintf(stderr,
697 "Warning: Valgrind is not installed. Memory check aborted!\n");
698 return MONKEY_NO;
699 }
700
701 MONKEY_asprintf(&cntxt->valgrind_output_tmp_file_name, "%d", rand());
702 cntxt->debug_mode = DEBUG_MODE_VALGRIND;
703 MONKEY_asprintf(&valgrindCommand,
704 "%s --leak-check=yes --log-file=%s %s", valgrindPath,
705 cntxt->valgrind_output_tmp_file_name,
706 cntxt->binary_name);
707 valgrindPipe = popen(valgrindCommand, "r");
708 if (NULL == valgrindPipe) {
709 fprintf(stderr, "Error in running Valgrind! Memory check aborted!\n");
710 MONKEY_free(valgrindCommand);
711 return MONKEY_NO;
712 }
713
714 pclose(valgrindPipe);
715 MONKEY_free(valgrindCommand);
716 return MONKEY_OK;
717}
718
719
720int MONKEY_ACTION_rerun_with_gdb(struct MONKEY_ACTION_Context
721 *cntxt)
722{
723 struct MONKEY_EDB_Context *edbCntxt;
724 struct stat buf;
725
726 /* Check if gdb is installed */
727 if (NULL == cntxt->gdb_binary_path) {
728 cntxt->gdb_binary_path = "/usr/bin/gdb"; /* Assumption for gdb default installation */
729 }
730 if (stat(cntxt->gdb_binary_path, &buf) != 0 && errno == ENOENT ) {
731 fprintf(stderr,
732 "Error: gdb is not installed!\n");
733 return MONKEY_NO;
734 }
735
736 epoch = NULL; /* Initializing epoch Data Structure */
737 cntxt->debug_mode = DEBUG_MODE_GDB;
738 /* This is like a file-handle for fopen.
739 Here we have all the state of gdb "connection". */
740 if (NULL != cntxt->gdb_binary_path)
741 mi_set_gdb_exe(cntxt->gdb_binary_path);
742 int ret;
743
744 /* Connect to gdb child. */
745 cntxt->gdb_handle = mi_connect_local();
746 if (!cntxt->gdb_handle) {
747 fprintf(stderr, "Error: failed to connect to gdb!\n");
748 return MONKEY_NO;
749 }
750 fprintf(stderr, "Info: Connected to gdb!\n");
751 cntxt->gdb_connected = MONKEY_YES;
752
753 /* Set all callbacks. */
754 mi_set_console_cb(cntxt->gdb_handle, cb_console, NULL);
755 mi_set_target_cb(cntxt->gdb_handle, cb_target, NULL);
756 mi_set_log_cb(cntxt->gdb_handle, cb_log, NULL);
757 mi_set_async_cb(cntxt->gdb_handle, cb_async, NULL);
758 mi_set_to_gdb_cb(cntxt->gdb_handle, cb_to, NULL);
759 mi_set_from_gdb_cb(cntxt->gdb_handle, cb_from, NULL);
760
761 /* Set the name of the child and the command line arguments. */
762 if (!gmi_set_exec(cntxt->gdb_handle, cntxt->binary_name, cntxt->binaryArgs)) {
763 fprintf(stderr, "Error: could not set gdb exec y args\n");
764 goto fail;
765 }
766
767 /* Tell gdb to attach the child to a terminal. */
768 if (!gmi_target_terminal(cntxt->gdb_handle, ttyname(STDIN_FILENO))) {
769 fprintf(stderr, "Error: could not select gdb target terminal\n");
770 goto fail;
771 }
772
773
774 if (MONKEY_YES == cntxt->run_reverse) {
775 /* If Backtracking is enabled, we must set a breakpoint at the main function, starting the program, then starting recording */
776 mi_bkpt *bp = gmi_break_insert_full(cntxt->gdb_handle, 0, 0, NULL, -1, -1, "main");
777 if (NULL == bp) {
778 fprintf(stderr, "Error: gdb could not set a breakpoint at function:%s\n",
779 cntxt->inspect_function);
780 goto fail;
781 }
782 mi_free_bkpt(bp);
783 }
784
785
786 if ((NULL != cntxt->inspect_expression) && (NULL != cntxt->inspect_function)) {
787 /* Setting a breakpoint at the function containing the expression to inspect */
788 mi_bkpt *bp = gmi_break_insert_full(cntxt->gdb_handle, 0, 0, NULL, -1, -1, cntxt->inspect_function);
789 if (NULL == bp) {
790 fprintf(stderr, "Error: gdb could not set a breakpoint at function:%s\n", cntxt->inspect_function);
791 goto fail;
792 }
793 mi_free_bkpt(bp);
794 }
795
796 /* Prepare a list of the file names for the source files we are analyzing */
797 edbCntxt = MONKEY_EDB_connect(cntxt->expression_database_path);
798 if (NULL == edbCntxt) {
799 fprintf(stderr,
800 "Unable to connect to Expression Database file!\n");
801 goto fail;
802 }
803
804 if (MONKEY_OK != MONKEY_EDB_get_file_names(edbCntxt, &iterateFileNames, NULL)) {
805 fprintf(stderr, "Error: Pathologist could not retrieve source file names from expression database!\n");
806 goto fail;
807 }
808 MONKEY_EDB_disconnect(edbCntxt);
809
810
811 /* Run the program. */
812 if (!gmi_exec_run(cntxt->gdb_handle)) {
813 fprintf(stderr, "Error: gdb could not run the target program!\n");
814 goto fail;
815 }
816
817 /* Backtracking is issued after running the program */
818 if (MONKEY_YES == cntxt->run_reverse) {
819 gmi_exec_record_process(cntxt->gdb_handle);
820 }
821
822 /* Here we should be stopped when the program crashes */
823 ret = wait_for_stop(cntxt);
824 if (ret == GDB_STATE_ERROR || ret == GDB_STATE_EXIT_NORMALLY) {
825 if (MONKEY_YES == cntxt->gdb_connected) {
826 mi_disconnect(cntxt->gdb_handle);
827 cntxt->gdb_connected = MONKEY_NO;
828 }
829 }
830
831 return ret;
832
833 // cleanup and abort
834fail:
835 if (MONKEY_YES == cntxt->gdb_connected) {
836 mi_disconnect(cntxt->gdb_handle);
837 cntxt->gdb_connected = MONKEY_NO;
838 }
839 return MONKEY_NO;
840}
841
842
843static const char *expressionListToString(struct Expression *head)
844{
845 char *string = MONKEY_strdup("");
846 char *strTmp;
847 struct Expression *tmp;
848
849 for (tmp = head; NULL != tmp; tmp = tmp->next) {
850 MONKEY_asprintf(&strTmp,
851 "%s%s => %s\n",
852 string,
853 tmp->expressionSyntax,
854 NULL ==
855 tmp->
856 expressionValue ? "Not evaluated" :
857 tmp->expressionValue);
858 MONKEY_free(string);
859 string = strTmp;
860 }
861 return string;
862}
863
864#if 0
865static int getWatchInfoListSize(struct WatchInfo *head)
866{
867 int count = 0;
868 int largestStr = 0;
869 struct WatchInfo *tmp = head;
870
871 while (NULL != tmp) {
872 if (largestStr < strlen(tmp->value))
873 largestStr = strlen(tmp->value);
874 tmp = tmp->next;
875 count++;
876 }
877
878 return count * largestStr;
879}
880
881static const char *watchInfoListToString(struct WatchInfo *head)
882{
883 char *string = MONKEY_malloc(getWatchInfoListSize(head));
884 char *strTmp;
885 struct WatchInfo *tmp = head;
886
887 MONKEY_asprintf(&strTmp, "%s\t \t%s\n", tmp->hitNumber, tmp->value);
888 strcpy(string, strTmp);
889 MONKEY_free(strTmp);
890 tmp = tmp->next;
891
892 while (NULL != tmp) {
893 MONKEY_asprintf(&strTmp, "%s\t \t%s\n", tmp->hitNumber,
894 tmp->value);
895 strcat(string, strTmp);
896 MONKEY_free(strTmp);
897 tmp = tmp->next;
898 }
899
900 return string;
901}
902#endif
903
904static const char *getValgrindOutput(struct MONKEY_ACTION_Context *cntxt)
905{
906 char *valgrindOutput;
907 int size;
908 FILE *valgrindFile = fopen(cntxt->valgrind_output_tmp_file_name, "r");
909 fseek(valgrindFile, 0L, SEEK_END);
910 size = ftell(valgrindFile);
911 fseek(valgrindFile, 0L, SEEK_SET);
912
913 valgrindOutput = MONKEY_malloc(size);
914 fread(valgrindOutput, size - 1, 1, valgrindFile);
915 fclose(valgrindFile);
916 return valgrindOutput;
917}
918
919
920static struct MONKEY_XML_Node *createXmlSimpleNode(const char *nodeName,
921 const char
922 *nodeInnerText)
923{
924 struct MONKEY_XML_Node *node =
925 MONKEY_XML_WRITER_new_node(nodeName, nodeInnerText);
926 return node;
927}
928
929
930static struct MONKEY_XML_Node *createXmlCrashNode(const char *category,
931 const char *function,
932 int line,
933 const char *file)
934{
935 struct MONKEY_XML_Node *node;
936 char *lineStr;
937
938 MONKEY_asprintf(&lineStr, "%d", line);
939 node = MONKEY_XML_WRITER_new_node("crash", NULL);
940 MONKEY_XML_WRITER_add_attribute(node, "category", category);
941 MONKEY_XML_WRITER_add_attribute(node, "function", function);
942 MONKEY_XML_WRITER_add_attribute(node, "line", lineStr);
943 MONKEY_XML_WRITER_add_attribute(node, "file", file);
944 return node;
945}
946
947
948static struct MONKEY_XML_Node *createXmlEpochStep(int step)
949{
950 struct MONKEY_XML_Node *node;
951 char *stepStr;
952 MONKEY_asprintf(&stepStr, "%d", step);
953 node = MONKEY_XML_WRITER_new_node("epoch", NULL);
954 MONKEY_XML_WRITER_add_attribute(node, "step", stepStr);
955 return node;
956}
957
958
959static struct MONKEY_XML_Node *createXmlFunctionNode(const char *name,
960 int line,
961 const char *file,
962 int depth)
963{
964 struct MONKEY_XML_Node *node;
965 char *lineStr;
966 char *depthStr;
967
968 MONKEY_asprintf(&lineStr, "%d", line);
969 MONKEY_asprintf(&depthStr, "%d", depth);
970 node = MONKEY_XML_WRITER_new_node("function", NULL);
971 MONKEY_XML_WRITER_add_attribute(node, "name", name);
972 MONKEY_XML_WRITER_add_attribute(node, "line", lineStr);
973 MONKEY_XML_WRITER_add_attribute(node, "file", file);
974 MONKEY_XML_WRITER_add_attribute(node, "depth", depthStr);
975 return node;
976}
977
978
979static struct MONKEY_XML_Node *createXmlExpressionNode(const char *name,
980 const char *value)
981{
982 struct MONKEY_XML_Node *node;
983
984 node = MONKEY_XML_WRITER_new_node("expression", value);
985 MONKEY_XML_WRITER_add_attribute(node, "name", name);
986 return node;
987}
988
989
990static struct MONKEY_XML_Node *createXmlGlobalExpressionNode(const char *name,
991 const char *value)
992{
993 struct MONKEY_XML_Node *node;
994
995 node = MONKEY_XML_WRITER_new_node("expression", value);
996 MONKEY_XML_WRITER_add_attribute(node, "name", name);
997 return node;
998}
999
1000
1001int MONKEY_ACTION_is_Nullpointer(struct MONKEY_ACTION_Context* cntxt)
1002{
1003 mi_asm_insns* disassembled_code = gmi_data_disassemble_se(cntxt->gdb_handle, "$pc", "$pc+1", 0);
1004 char* reg;
1005 while(reg = strstr(disassembled_code->ins->inst, "(%")) {
1006 //printf("%s\n", reg);
1007 reg++;
1008 size_t len = strcspn(reg, "),");
1009 reg[0]='$';
1010 reg[len]='\0';
1011 char* val = gmi_data_evaluate_expression(cntxt->gdb_handle, reg);
1012 //printf("%s\n", val);
1013 if( !strcmp(val, "0") ) return 1;
1014 }
1015 return 0;
1016}
1017
1018
1019int MONKEY_ACTION_format_report_xml(struct MONKEY_ACTION_Context
1020 *cntxt)
1021{
1022 int i = 0;
1023 struct MONKEY_XML_Node *node;
1024 struct MONKEY_XML_Node *historyNode;
1025 struct MONKEY_XML_Node *traceNode;
1026 struct MONKEY_XML_Node *globalsNode;
1027 struct Trace *tracePtr = epoch->traceListHead;
1028 struct Function *functionPtr = tracePtr->functionListHead;
1029 struct Expression *expressionPtr;
1030
1031
1032 switch (cntxt->bug_detected) {
1033 case BUG_NULL_POINTER:
1034 cntxt->xml_report_node = createXmlCrashNode("npe", functionPtr->name, functionPtr->line, functionPtr->file);
1035 break;
1036 case BUG_BAD_MEM_ACCESS:
1037 cntxt->xml_report_node = createXmlCrashNode("Bad memory access", functionPtr->name, functionPtr->line, functionPtr->file);
1038 node = MONKEY_XML_WRITER_add_child(cntxt->xml_report_node, createXmlSimpleNode("valgrind", getValgrindOutput(cntxt)));
1039 break;
1040 case BUG_ABORT:
1041 cntxt->xml_report_node = createXmlCrashNode("Assertion Failure", functionPtr->name, functionPtr->line, functionPtr->file);
1042 break;
1043 case BUG_ARITHMETIC:
1044 cntxt->xml_report_node = createXmlCrashNode("Division By Zero", functionPtr->name, functionPtr->line, functionPtr->file);
1045 break;
1046 case BUG_SIG_BUS:
1047 cntxt->xml_report_node = createXmlCrashNode("Bus Error", functionPtr->name, functionPtr->line, functionPtr->file);
1048 break;
1049 default:
1050 cntxt->xml_report_node = createXmlCrashNode("Unknown", functionPtr->name, functionPtr->line, functionPtr->file);
1051 break;
1052 }
1053
1054 historyNode = MONKEY_XML_WRITER_add_child(cntxt->xml_report_node, createXmlSimpleNode("history", NULL));
1055
1056 /* Adding Stack Trace Nodes to XML Report */
1057 while (NULL != tracePtr) {
1058 node = MONKEY_XML_WRITER_add_child(historyNode, createXmlEpochStep(i));
1059 traceNode = MONKEY_XML_WRITER_add_child(node, createXmlSimpleNode("trace", NULL));
1060 globalsNode = MONKEY_XML_WRITER_add_child(node, createXmlSimpleNode("globals", NULL));
1061
1062 functionPtr = tracePtr->functionListHead;
1063 while (NULL != functionPtr) {
1064 node = MONKEY_XML_WRITER_add_child(traceNode, createXmlFunctionNode(functionPtr->name,
1065 functionPtr->line,
1066 functionPtr->file,
1067 functionPtr->depth));
1068 node = MONKEY_XML_WRITER_add_child(node, createXmlSimpleNode("expressions", NULL));
1069
1070 expressionPtr = functionPtr->expressionListHead;
1071 while (NULL != expressionPtr) {
1072 // ignore non helpful expressions
1073 if( expressionPtr->expressionValue )
1074 MONKEY_XML_WRITER_add_child(node, createXmlExpressionNode(expressionPtr->expressionSyntax, expressionPtr->expressionValue));
1075 expressionPtr = expressionPtr->next;
1076 }
1077
1078 functionPtr = functionPtr->next;
1079 }
1080
1081 expressionPtr = tracePtr->globalsListHead;
1082 while( expressionPtr ) {
1083 if( expressionPtr->expressionValue )
1084 MONKEY_XML_WRITER_add_child(globalsNode, createXmlGlobalExpressionNode(expressionPtr->expressionSyntax, expressionPtr->expressionValue));
1085 expressionPtr = expressionPtr->next;
1086 }
1087
1088
1089 tracePtr = tracePtr->next;
1090 i++;
1091 }
1092 return MONKEY_OK;
1093}
1094
1095
1096
1097
1098int MONKEY_ACTION_delete_context(struct MONKEY_ACTION_Context
1099 *cntxt)
1100{
1101 if (MONKEY_YES == cntxt->gdb_connected) {
1102 mi_disconnect(cntxt->gdb_handle);
1103 cntxt->gdb_connected = MONKEY_NO;
1104 }
1105 if (NULL != cntxt->debug_report)
1106 MONKEY_free(cntxt->debug_report);
1107 if (NULL != cntxt->valgrind_output_tmp_file_name) {
1108 remove(cntxt->valgrind_output_tmp_file_name);
1109 MONKEY_free(cntxt->valgrind_output_tmp_file_name);
1110 }
1111 if (NULL != cntxt->xml_report_node)
1112 MONKEY_XML_WRITER_delete_tree(cntxt->xml_report_node);
1113 if (NULL != cntxt->binaryArgs)
1114 MONKEY_free(cntxt->binaryArgs);
1115
1116 MONKEY_free(cntxt);
1117 return MONKEY_OK;
1118}
1119
1120
1121int MONKEY_ACTION_check_bug_redundancy()
1122{
1123 return MONKEY_OK;
1124}
diff --git a/pathologist/src/pathologist/action.h b/pathologist/src/pathologist/action.h
new file mode 100644
index 0000000..57b748b
--- /dev/null
+++ b/pathologist/src/pathologist/action.h
@@ -0,0 +1,100 @@
1/**
2 * @file monkey/MONKEY_action.h
3 * @brief Monkey API for actions taken by Monkey while debugging
4 */
5
6#ifndef MONKEY_ACTION_H
7#define MONKEY_ACTION_H
8
9#include "gdbmi.h"
10#include "pathologist_xml_writer.h"
11
12#ifdef __cplusplus
13extern "C"
14{
15#if 0 /* keep Emacsens' auto-indent happy */
16}
17#endif
18#endif
19
20
21/* Debug constants */
22#define DEBUG_MODE_GDB 0
23#define GDB_STATE_STOPPED 1
24#define GDB_STATE_EXIT_NORMALLY 2
25#define GDB_STATE_ERROR 3
26#define GDB_SAFETY_COUNT 2
27#define DEBUG_MODE_VALGRIND 4
28#define DEBUG_MODE_REPORT_READY 5
29#define NO_BUG_DETECTED -1
30#define BUG_NULL_POINTER 6
31#define BUG_ABORT 7
32#define BUG_ARITHMETIC 8
33#define BUG_BAD_MEM_ACCESS 9
34#define BUG_SIG_BUS 10
35#define GDB_MI_ASYNC_WAIT 5
36
37/**
38 * Context for the Action API
39 */
40struct MONKEY_ACTION_Context
41{
42 const char *binary_name;
43 char *binaryArgs;
44 const char *email_address;
45 const char *expression_database_path;
46 const char *gdb_binary_path;
47 const char *valgrind_binary_path;
48 const char *inspect_expression;
49 const char *inspect_function;
50 int debug_mode;
51 int bug_detected;
52 int has_null;
53 char *debug_report;
54 struct MONKEY_XML_Node *xml_report_node;
55
56 /* gdb debugging attributes */
57 int run_reverse;
58 int scope_depth;
59 mi_h *gdb_handle;
60 int gdb_connected;
61 const char *gdb_in_use;
62 mi_stop *gdb_stop_reason;
63 mi_frames *gdb_frames;
64 const char *gdb_null_variable;
65
66 /* Valgrind memcheck attributes */
67 char* valgrind_output_tmp_file_name;
68};
69
70
71int MONKEY_ACTION_report_file (struct MONKEY_ACTION_Context
72 *cntxt, const char *dumpFileName, int isXML);
73int MONKEY_ACTION_report_email (struct MONKEY_ACTION_Context
74 *cntxt, const char *dumpFileName);
75int MONKEY_ACTION_inspect_expression_database (struct
76 MONKEY_ACTION_Context
77 *cntxt);
78int MONKEY_ACTION_rerun_with_gdb (struct MONKEY_ACTION_Context
79 *cntxt);
80int MONKEY_ACTION_start_reverse_execution(struct MONKEY_ACTION_Context * cntxt);
81int MONKEY_ACTION_rerun_with_valgrind (struct
82 MONKEY_ACTION_Context
83 *cntxt);
84int MONKEY_ACTION_format_report (struct MONKEY_ACTION_Context
85 *cntxt);
86int
87MONKEY_ACTION_format_report_xml (struct MONKEY_ACTION_Context
88 *cntxt);
89int MONKEY_ACTION_delete_context(struct MONKEY_ACTION_Context *cntxt);
90
91int MONKEY_ACTION_check_bug_redundancy (void);
92
93
94#if 0 /* keep Emacsens' auto-indent happy */
95{
96#endif
97#ifdef __cplusplus
98}
99#endif
100#endif
diff --git a/pathologist/src/pathologist/edb_api.c b/pathologist/src/pathologist/edb_api.c
new file mode 100644
index 0000000..5af26de
--- /dev/null
+++ b/pathologist/src/pathologist/edb_api.c
@@ -0,0 +1,368 @@
1/**
2 * @file monkey/edb_api.c
3 * @brief Monkey API for accessing the Expression Database (edb)
4 */
5
6#include "monkey_common.h"
7#include <stdlib.h>
8#include <stdio.h>
9#include <sqlite3.h>
10#include "pathologist_edb.h"
11
12
13/**
14 * Context for Database connection and Expressions
15 */
16struct MONKEY_EDB_Context
17{
18 /**
19 * Database connection
20 */
21 sqlite3 *db_handle;
22};
23
24
25/**
26 * Establish a connection to the Expression Database
27 *
28 * @param db_file_name path the Expression Database file
29 * @return context to use for Accessing the Expression Database, NULL on error
30 */
31struct MONKEY_EDB_Context *
32MONKEY_EDB_connect (const char *db_file_name)
33{
34 int err;
35 struct MONKEY_EDB_Context *ctxt =
36 MONKEY_malloc (sizeof (struct MONKEY_EDB_Context));
37
38 err = sqlite3_open (db_file_name, &ctxt->db_handle);
39 if (err)
40 {
41 fprintf(stderr,
42 "Cannot open Expression Database. `%s'\n",
43 sqlite3_errmsg (ctxt->db_handle));
44 return NULL;
45 }
46 return ctxt;
47}
48
49
50/**
51 * Disconnect from Database, and cleanup resources
52 *
53 * @param context context containing the Expression Database handle
54 * @return MONKEY_OK on success, MONKEY_NO on failure
55 */
56int
57MONKEY_EDB_disconnect (struct MONKEY_EDB_Context *cntxt)
58{
59 sqlite3_close (cntxt->db_handle);
60 MONKEY_free (cntxt);
61 return MONKEY_OK;
62}
63
64
65
66int
67MONKEY_EDB_get_file_names (struct MONKEY_EDB_Context *cntxt,
68 MONKEY_FileIterator iter,
69 void *iter_cls)
70{
71 int err;
72 char *errMsg;
73 char *query;
74
75 if (MONKEY_asprintf (&query, "select distinct file_name from Expression") == -1)
76 {
77 fprintf(stderr,
78 "Memory allocation problem occurred during creating database query!\n");
79 return MONKEY_NO;
80 }
81
82 err = sqlite3_exec (cntxt->db_handle, query, iter, iter_cls, &errMsg);
83 if (err)
84 {
85 fprintf(stderr,
86 "Error occurred while executing Database query. `%s'",
87 errMsg);
88 return MONKEY_NO;
89 }
90 return MONKEY_OK;
91}
92
93
94
95int
96MONKEY_EDB_get_all_outer_scopes(struct MONKEY_EDB_Context
97 *cntxt, const char *file_name,
98 int function_beginning,
99 int scope_in_question_start,
100 int scope_in_question_end,
101 MONKEY_ExpressionIterator
102 iter, void *iter_cls)
103{
104 int err;
105 char *errMsg;
106 char *query;
107
108 if (MONKEY_asprintf
109 (&query,
110 "select distinct end_lineno from Expression where file_name LIKE \'%%%s\' and start_lineno >= %d and start_lineno < %d and end_lineno > %d order by end_lineno",
111 file_name, function_beginning, scope_in_question_start, scope_in_question_end) == -1)
112 {
113 fprintf(stderr,
114 "Memory allocation problem occurred during creating database query!\n");
115 return MONKEY_NO;
116 }
117
118 err = sqlite3_exec (cntxt->db_handle, query, iter, iter_cls, &errMsg);
119 if (err)
120 {
121 fprintf(stderr,
122 "Error occurred while executing Database query. `%s'",
123 errMsg);
124 return MONKEY_NO;
125 }
126 return MONKEY_OK;
127}
128
129int
130MONKEY_EDB_expression_function_end_scope(struct MONKEY_EDB_Context
131 *cntxt, const char *file_name,
132 int start_lineno,
133 MONKEY_ExpressionIterator
134 iter, void *iter_cls)
135{
136 int err;
137 char *errMsg;
138 char *query;
139
140 if (MONKEY_asprintf
141 (&query,
142 "select MAX(end_lineno) from Expression where file_name LIKE \'%%%s\' and start_lineno < %d and end_lineno != 32767",
143 file_name, start_lineno) == -1)
144 {
145 fprintf(stderr,
146 "Memory allocation problem occurred during creating database query!\n");
147 return MONKEY_NO;
148 }
149
150 err = sqlite3_exec (cntxt->db_handle, query, iter, iter_cls, &errMsg);
151 if (err)
152 {
153 fprintf(stderr,
154 "Error occurred while executing Database query. `%s'",
155 errMsg);
156 return MONKEY_NO;
157 }
158 return MONKEY_OK;
159}
160
161int
162MONKEY_EDB_function_start_line_for_scope(struct MONKEY_EDB_Context
163 *cntxt, const char *file_name,
164 int scope_end,
165 MONKEY_ExpressionIterator
166 iter, void *iter_cls)
167{
168 int err;
169 char *errMsg;
170 char *query;
171
172 if (MONKEY_asprintf
173 (&query,
174 "select MIN(start_lineno) from Expression where file_name LIKE \'%%%s\' and end_lineno >= %d",
175 file_name, scope_end) == -1)
176 {
177 fprintf(stderr,
178 "Memory allocation problem occurred during creating database query!\n");
179 return MONKEY_NO;
180 }
181
182 err = sqlite3_exec (cntxt->db_handle, query, iter, iter_cls, &errMsg);
183 if (err)
184 {
185 fprintf(stderr,
186 "Error occurred while executing Database query. `%s'",
187 errMsg);
188 return MONKEY_NO;
189 }
190 return MONKEY_OK;
191}
192
193
194/**
195 * Return the line number of the end-of-scope for the expression indicated by start_line_no
196 *
197 * @param cntxt context containing the Expression Database handle
198 * @param file_name path to the file in which the expression in question exists
199 * @param start_line_no expression's line
200 * @param iter callback function, iterator for values returned from the Database
201 * @param iter_cls closure for the expression iterator, will contain the scope-end line number
202 * @return MONKEY_OK on success, MONKEY_NO on failure
203 */
204int
205MONKEY_EDB_get_expression_scope_end (struct MONKEY_EDB_Context
206 *cntxt, const char *file_name,
207 int start_line_no,
208 MONKEY_ExpressionIterator
209 iter, void *iter_cls)
210{
211 int err;
212 char *errMsg;
213 char *query;
214
215 if (MONKEY_asprintf
216 (&query,
217 "select min(end_lineno) from Expression where file_name LIKE \'%%%s\' and start_lineno = %d",
218 file_name, start_line_no) == -1)
219 {
220 fprintf(stderr,
221 "Memory allocation problem occurred during creating database query!\n");
222 return MONKEY_NO;
223 }
224
225 err = sqlite3_exec (cntxt->db_handle, query, iter, iter_cls, &errMsg);
226 MONKEY_free (query);
227
228 if (err)
229 {
230 fprintf(stderr,
231 "Error occurred while executing Database query. `%s'",
232 errMsg);
233 return MONKEY_NO;
234 }
235 return MONKEY_OK;
236}
237
238
239int
240MONKEY_EDB_get_expression_scope_end_for_expression (struct MONKEY_EDB_Context
241 *cntxt, const char *file_name,
242 const char * expr_syntax,
243 MONKEY_ExpressionIterator
244 iter, void *iter_cls)
245{
246 int err;
247 char *errMsg;
248 char *query;
249
250 if (MONKEY_asprintf
251 (&query,
252 "select end_lineno from Expression where file_name LIKE \'%%%s\' and expr_syntax = %s",
253 file_name, expr_syntax) == -1)
254 {
255 fprintf(stderr,
256 "Memory allocation problem occurred during creating database query!\n");
257 return MONKEY_NO;
258 }
259
260 err = sqlite3_exec (cntxt->db_handle, query, iter, iter_cls, &errMsg);
261 MONKEY_free (query);
262
263 if (err)
264 {
265 fprintf(stderr,
266 "Error occurred while executing Database query. `%s'",
267 errMsg);
268 return MONKEY_NO;
269 }
270 return MONKEY_OK;
271}
272
273
274int
275MONKEY_EDB_get_globals( struct MONKEY_EDB_Context *cntxt,
276 const char *file_names,
277 MONKEY_ExpressionIterator iter,
278 void *iter_cls) {
279 int ret = MONKEY_OK;
280 char *errMsg;
281 char *query;
282 if( MONKEY_asprintf(&query,
283 "SELECT DISTINCT expr_syntax, is_call FROM Expression WHERE (%s) AND start_lineno = 0 AND end_lineno = 32767",
284 file_names) == -1 ) {
285 fprintf(stderr, "Memory allocation problem occurred!\n");
286 ret = MONKEY_NO;
287 goto fin;
288 }
289
290 if( sqlite3_exec (cntxt->db_handle, query, iter, iter_cls, &errMsg) ) {
291 fprintf(stderr, "Error occurred while executing Database query. `%s'", errMsg);
292 ret = MONKEY_NO;
293 goto fin;
294 }
295fin:
296 MONKEY_free_non_null(query);
297 return MONKEY_NO;
298}
299
300/**
301 * Run an SQLite query to retrieve those expressions that are previous to
302 * given expression and are in the same function of the given expression
303 *
304 * @param cntxt context containing the Expression Database handle
305 * @param file_name path to the file in which the expression in question exists
306 * @param start_line_no expression beginning line
307 * @param iter callback function, iterator for expressions returned from the Database
308 * @param iter_cls closure for the expression iterator
309 * @return MONKEY_OK success, MONKEY_NO failure
310 */
311int
312MONKEY_EDB_get_expressions (struct MONKEY_EDB_Context *cntxt,
313 const char *file_name, int start_line_no,
314 MONKEY_ExpressionIterator iter,
315 void *iter_cls)
316{
317 int err;
318 char *errMsg;
319 char *query;
320 if (MONKEY_asprintf
321 (&query,
322 "select distinct expr_syntax, is_call from Expression where file_name LIKE \'%%%s\' and start_lineno <= %d and end_lineno >= %d and end_lineno != 32767",
323 file_name, start_line_no, start_line_no) == -1)
324 {
325 fprintf(stderr, "Memory allocation problem occurred!\n");
326 return MONKEY_NO;
327 }
328
329 err = sqlite3_exec (cntxt->db_handle, query, iter, iter_cls, &errMsg);
330 if (err)
331 {
332 fprintf(stderr, "Error occurred while executing Database query. `%s'\n", errMsg);
333 return MONKEY_NO;
334 }
335 return MONKEY_OK;
336}
337
338
339int
340MONKEY_EDB_get_sub_expressions (struct MONKEY_EDB_Context
341 *cntxt, const char *file_name,
342 int start_line_no, int end_line_no,
343 MONKEY_ExpressionIterator iter,
344 void *iter_cls)
345{
346 int err;
347 char *errMsg;
348 char *query;
349 if (MONKEY_asprintf
350 (&query,
351 "select expr_syntax, start_lineno, is_call from Expression where file_name LIKE \'%%%s\' and start_lineno = %d and end_lineno = %d",
352 file_name, start_line_no, end_line_no) == -1)
353 {
354 fprintf(stderr,
355 "Memory allocation problem occurred!\n");
356 return MONKEY_NO;
357 }
358
359 err = sqlite3_exec (cntxt->db_handle, query, iter, iter_cls, &errMsg);
360 if (err)
361 {
362 fprintf(stderr,
363 "Error occurred while executing Database query. `%s'",
364 errMsg);
365 return MONKEY_NO;
366 }
367 return MONKEY_OK;
368}
diff --git a/pathologist/src/pathologist/getopt.c b/pathologist/src/pathologist/getopt.c
new file mode 100644
index 0000000..d7a037c
--- /dev/null
+++ b/pathologist/src/pathologist/getopt.c
@@ -0,0 +1,1047 @@
1/* Getopt for GNU.
2 NOTE: getopt is now part of the C library, so if you don't know what
3 "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
4 before changing it!
5
6 Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
7 Free Software Foundation, Inc.
8
9NOTE: The canonical source of this file is maintained with the GNU C Library.
10Bugs can be reported to bug-glibc@prep.ai.mit.edu.
11
12This program is free software; you can redistribute it and/or modify it
13under the terms of the GNU General Public License as published by the
14Free Software Foundation; either version 2, or (at your option) any
15later version.
16
17This program is distributed in the hope that it will be useful,
18but WITHOUT ANY WARRANTY; without even the implied warranty of
19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20GNU General Public License for more details.
21
22You should have received a copy of the GNU General Public License
23along with this program; if not, write to the Free Software
24Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25USA.
26
27
28This code was heavily modified for GNUnet.
29Copyright (C) 2006 Christian Grothoff
30*/
31
32/**
33 * @file util/getopt.c
34 * @brief GNU style option parsing
35 *
36 * TODO: get rid of statics (make reentrant) and
37 * replace main GNU getopt parser with one that
38 * actually fits our API.
39 */
40
41#include "platform.h"
42#include "monkey_common.h"
43#include "monkey_getopt_lib.h"
44
45#ifdef VMS
46#include <unixlib.h>
47#if HAVE_STRING_H - 0
48#include <string.h>
49#endif
50#endif
51
52#define LOG(kind,...) MONKEY_log_from (kind, "util", __VA_ARGS__)
53
54#define LOG_STRERROR(kind,syscall) MONKEY_log_from_strerror (kind, "util", syscall)
55
56#if defined (WIN32) && !defined (__CYGWIN32__)
57/* It's not Unix, really. See? Capital letters. */
58#include <windows.h>
59#define getpid() GetCurrentProcessId()
60#endif
61
62#ifndef _
63/* This is for other GNU distributions with internationalized messages.
64 When compiling libc, the _ macro is predefined. */
65#ifdef HAVE_LIBINTL_H
66#include <libintl.h>
67#define _(msgid) gettext (msgid)
68#else
69#define _(msgid) (msgid)
70#endif
71#endif
72
73/* Describe the long-named options requested by the application.
74 The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
75 of `struct GNoption' terminated by an element containing a name which is
76 zero.
77
78 The field `has_arg' is:
79 no_argument (or 0) if the option does not take an argument,
80 required_argument (or 1) if the option requires an argument,
81 optional_argument (or 2) if the option takes an optional argument.
82
83 If the field `flag' is not NULL, it points to a variable that is set
84 to the value given in the field `val' when the option is found, but
85 left unchanged if the option is not found.
86
87 To have a long-named option do something other than set an `int' to
88 a compiled-in constant, such as set a value from `GNoptarg', set the
89 option's `flag' field to zero and its `val' field to a nonzero
90 value (the equivalent single-letter option character, if there is
91 one). For long options that have a zero `flag' field, `getopt'
92 returns the contents of the `val' field. */
93
94struct GNoption
95{
96 const char *name;
97 /* has_arg can't be an enum because some compilers complain about
98 * type mismatches in all the code that assumes it is an int. */
99 int has_arg;
100 int *flag;
101 int val;
102};
103
104
105/* This version of `getopt' appears to the caller like standard Unix `getopt'
106 but it behaves differently for the user, since it allows the user
107 to intersperse the options with the other arguments.
108
109 As `getopt' works, it permutes the elements of ARGV so that,
110 when it is done, all the options precede everything else. Thus
111 all application programs are extended to handle flexible argument order.
112
113 Setting the environment variable POSIXLY_CORRECT disables permutation.
114 Then the behavior is completely standard.
115
116 GNU application programs can use a third alternative mode in which
117 they can distinguish the relative order of options and other arguments. */
118
119/* For communication from `getopt' to the caller.
120 When `getopt' finds an option that takes an argument,
121 the argument value is returned here.
122 Also, when `ordering' is RETURN_IN_ORDER,
123 each non-option ARGV-element is returned here. */
124
125static char *GNoptarg = NULL;
126
127/* Index in ARGV of the next element to be scanned.
128 This is used for communication to and from the caller
129 and for communication between successive calls to `getopt'.
130
131 On entry to `getopt', zero means this is the first call; initialize.
132
133 When `getopt' returns -1, this is the index of the first of the
134 non-option elements that the caller should itself scan.
135
136 Otherwise, `GNoptind' communicates from one call to the next
137 how much of ARGV has been scanned so far. */
138
139/* 1003.2 says this must be 1 before any call. */
140static int GNoptind = 1;
141
142/* The next char to be scanned in the option-element
143 in which the last option character we returned was found.
144 This allows us to pick up the scan where we left off.
145
146 If this is zero, or a null string, it means resume the scan
147 by advancing to the next ARGV-element. */
148
149static char *nextchar;
150
151
152/* Describe how to deal with options that follow non-option ARGV-elements.
153
154 If the caller did not specify anything,
155 the default is REQUIRE_ORDER if the environment variable
156 POSIXLY_CORRECT is defined, PERMUTE otherwise.
157
158 REQUIRE_ORDER means don't recognize them as options;
159 stop option processing when the first non-option is seen.
160 This is what Unix does.
161 This mode of operation is selected by either setting the environment
162 variable POSIXLY_CORRECT, or using `+' as the first character
163 of the list of option characters.
164
165 PERMUTE is the default. We MONKEY_CRYPTO_random_permute the contents of ARGV as we scan,
166 so that eventually all the non-options are at the end. This allows options
167 to be given in any order, even with programs that were not written to
168 expect this.
169
170 RETURN_IN_ORDER is an option available to programs that were written
171 to expect GNoptions and other ARGV-elements in any order and that care about
172 the ordering of the two. We describe each non-option ARGV-element
173 as if it were the argument of an option with character code 1.
174 Using `-' as the first character of the list of option characters
175 selects this mode of operation.
176
177 The special argument `--' forces an end of option-scanning regardless
178 of the value of `ordering'. In the case of RETURN_IN_ORDER, only
179 `--' can cause `getopt' to return -1 with `GNoptind' != ARGC. */
180
181static enum
182{
183 REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
184} ordering;
185
186/* Value of POSIXLY_CORRECT environment variable. */
187static char *posixly_correct;
188
189#ifdef __GNU_LIBRARY__
190/* We want to avoid inclusion of string.h with non-GNU libraries
191 because there are many ways it can cause trouble.
192 On some systems, it contains special magic macros that don't work
193 in GCC. */
194#include <string.h>
195#define my_index strchr
196#else
197
198/* Avoid depending on library functions or files
199 whose names are inconsistent. */
200
201char *
202getenv ();
203
204static char *
205my_index (const char *str,
206 int chr)
207{
208 while (*str)
209 {
210 if (*str == chr)
211 return (char *) str;
212 str++;
213 }
214 return 0;
215}
216
217/* If using GCC, we can safely declare strlen this way.
218 If not using GCC, it is ok not to declare it. */
219#ifdef __GNUC__
220/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
221 That was relevant to code that was here before. */
222#if !defined (__STDC__) || !__STDC__
223/* gcc with -traditional declares the built-in strlen to return int,
224 and has done so at least since version 2.4.5. -- rms. */
225extern int
226strlen (const char *);
227#endif /* not __STDC__ */
228#endif /* __GNUC__ */
229
230#endif /* not __GNU_LIBRARY__ */
231
232/* Handle permutation of arguments. */
233
234/* Describe the part of ARGV that contains non-options that have
235 been skipped. `first_nonopt' is the index in ARGV of the first of them;
236 `last_nonopt' is the index after the last of them. */
237
238static int first_nonopt;
239static int last_nonopt;
240
241#ifdef _LIBC
242/* Bash 2.0 gives us an environment variable containing flags
243 indicating ARGV elements that should not be considered arguments. */
244
245/* Defined in getopt_init.c */
246extern char *__getopt_nonoption_flags;
247
248static int nonoption_flags_max_len;
249static int nonoption_flags_len;
250
251static int original_argc;
252static char *const *original_argv;
253
254extern pid_t __libc_pid;
255
256/* Make sure the environment variable bash 2.0 puts in the environment
257 is valid for the getopt call we must make sure that the ARGV passed
258 to getopt is that one passed to the process. */
259static void MONKEY_UNUSED
260store_args_and_env (int argc, char *const *argv)
261{
262 /* XXX This is no good solution. We should rather copy the args so
263 * that we can compare them later. But we must not use malloc(3). */
264 original_argc = argc;
265 original_argv = argv;
266}
267
268text_set_element (__libc_subinit, store_args_and_env);
269
270#define SWAP_FLAGS(ch1, ch2) \
271 if (nonoption_flags_len > 0) \
272 { \
273 char __tmp = __getopt_nonoption_flags[ch1]; \
274 __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
275 __getopt_nonoption_flags[ch2] = __tmp; \
276 }
277#else /* !_LIBC */
278#define SWAP_FLAGS(ch1, ch2)
279#endif /* _LIBC */
280
281/* Exchange two adjacent subsequences of ARGV.
282 One subsequence is elements [first_nonopt,last_nonopt)
283 which contains all the non-options that have been skipped so far.
284 The other is elements [last_nonopt,GNoptind), which contains all
285 the options processed since those non-options were skipped.
286
287 `first_nonopt' and `last_nonopt' are relocated so that they describe
288 the new indices of the non-options in ARGV after they are moved. */
289
290#if defined (__STDC__) && __STDC__
291static void
292exchange (char **);
293#endif
294
295static void
296exchange (char **argv)
297{
298 int bottom = first_nonopt;
299 int middle = last_nonopt;
300 int top = GNoptind;
301 char *tem;
302
303 /* Exchange the shorter segment with the far end of the longer segment.
304 * That puts the shorter segment into the right place.
305 * It leaves the longer segment in the right place overall,
306 * but it consists of two parts that need to be swapped next. */
307
308#ifdef _LIBC
309 /* First make sure the handling of the `__getopt_nonoption_flags'
310 * string can work normally. Our top argument must be in the range
311 * of the string. */
312 if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
313 {
314 /* We must extend the array. The user plays games with us and
315 * presents new arguments. */
316 char *new_str = malloc (top + 1);
317
318 if (new_str == NULL)
319 nonoption_flags_len = nonoption_flags_max_len = 0;
320 else
321 {
322 memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len);
323 memset (&new_str[nonoption_flags_max_len], '\0',
324 top + 1 - nonoption_flags_max_len);
325 nonoption_flags_max_len = top + 1;
326 __getopt_nonoption_flags = new_str;
327 }
328 }
329#endif
330
331 while (top > middle && middle > bottom)
332 {
333 if (top - middle > middle - bottom)
334 {
335 /* Bottom segment is the short one. */
336 int len = middle - bottom;
337 register int i;
338
339 /* Swap it with the top part of the top segment. */
340 for (i = 0; i < len; i++)
341 {
342 tem = argv[bottom + i];
343 argv[bottom + i] = argv[top - (middle - bottom) + i];
344 argv[top - (middle - bottom) + i] = tem;
345 SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
346 }
347 /* Exclude the moved bottom segment from further swapping. */
348 top -= len;
349 }
350 else
351 {
352 /* Top segment is the short one. */
353 int len = top - middle;
354 register int i;
355
356 /* Swap it with the bottom part of the bottom segment. */
357 for (i = 0; i < len; i++)
358 {
359 tem = argv[bottom + i];
360 argv[bottom + i] = argv[middle + i];
361 argv[middle + i] = tem;
362 SWAP_FLAGS (bottom + i, middle + i);
363 }
364 /* Exclude the moved top segment from further swapping. */
365 bottom += len;
366 }
367 }
368
369 /* Update records for the slots the non-options now occupy. */
370
371 first_nonopt += (GNoptind - last_nonopt);
372 last_nonopt = GNoptind;
373}
374
375/* Initialize the internal data when the first call is made. */
376
377#if defined (__STDC__) && __STDC__
378static const char *
379_getopt_initialize (int, char *const *, const char *);
380#endif
381static const char *
382_getopt_initialize (int argc,
383 char *const *argv,
384 const char *optstring)
385{
386 /* Start processing options with ARGV-element 1 (since ARGV-element 0
387 * is the program name); the sequence of previously skipped
388 * non-option ARGV-elements is empty. */
389
390 first_nonopt = last_nonopt = GNoptind;
391
392 nextchar = NULL;
393
394 posixly_correct = getenv ("POSIXLY_CORRECT");
395
396 /* Determine how to handle the ordering of options and nonoptions. */
397
398 if (optstring[0] == '-')
399 {
400 ordering = RETURN_IN_ORDER;
401 ++optstring;
402 }
403 else if (optstring[0] == '+')
404 {
405 ordering = REQUIRE_ORDER;
406 ++optstring;
407 }
408 else if (posixly_correct != NULL)
409 ordering = REQUIRE_ORDER;
410 else
411 ordering = PERMUTE;
412
413#ifdef _LIBC
414 if (posixly_correct == NULL && argc == original_argc && argv == original_argv)
415 {
416 if (nonoption_flags_max_len == 0)
417 {
418 if (__getopt_nonoption_flags == NULL ||
419 __getopt_nonoption_flags[0] == '\0')
420 nonoption_flags_max_len = -1;
421 else
422 {
423 const char *orig_str = __getopt_nonoption_flags;
424 int len = nonoption_flags_max_len = strlen (orig_str);
425
426 if (nonoption_flags_max_len < argc)
427 nonoption_flags_max_len = argc;
428 __getopt_nonoption_flags = (char *) malloc (nonoption_flags_max_len);
429 if (__getopt_nonoption_flags == NULL)
430 nonoption_flags_max_len = -1;
431 else
432 {
433 memcpy (__getopt_nonoption_flags, orig_str, len);
434 memset (&__getopt_nonoption_flags[len], '\0',
435 nonoption_flags_max_len - len);
436 }
437 }
438 }
439 nonoption_flags_len = nonoption_flags_max_len;
440 }
441 else
442 nonoption_flags_len = 0;
443#endif
444
445 return optstring;
446}
447
448/* Scan elements of ARGV (whose length is ARGC) for option characters
449 given in OPTSTRING.
450
451 If an element of ARGV starts with '-', and is not exactly "-" or "--",
452 then it is an option element. The characters of this element
453 (aside from the initial '-') are option characters. If `getopt'
454 is called repeatedly, it returns successively each of the option characters
455 from each of the option elements.
456
457 If `getopt' finds another option character, it returns that character,
458 updating `GNoptind' and `nextchar' so that the next call to `getopt' can
459 resume the scan with the following option character or ARGV-element.
460
461 If there are no more option characters, `getopt' returns -1.
462 Then `GNoptind' is the index in ARGV of the first ARGV-element
463 that is not an option. (The ARGV-elements have been permuted
464 so that those that are not options now come last.)
465
466 OPTSTRING is a string containing the legitimate option characters.
467 If an option character is seen that is not listed in OPTSTRING,
468 return '?' after printing an error message. If you set `GNopterr' to
469 zero, the error message is suppressed but we still return '?'.
470
471 If a char in OPTSTRING is followed by a colon, that means it wants an arg,
472 so the following text in the same ARGV-element, or the text of the following
473 ARGV-element, is returned in `GNoptarg'. Two colons mean an option that
474 wants an optional arg; if there is text in the current ARGV-element,
475 it is returned in `GNoptarg', otherwise `GNoptarg' is set to zero.
476
477 If OPTSTRING starts with `-' or `+', it requests different methods of
478 handling the non-option ARGV-elements.
479 See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
480
481 Long-named options begin with `--' instead of `-'.
482 Their names may be abbreviated as long as the abbreviation is unique
483 or is an exact match for some defined option. If they have an
484 argument, it follows the option name in the same ARGV-element, separated
485 from the option name by a `=', or else the in next ARGV-element.
486 When `getopt' finds a long-named option, it returns 0 if that option's
487 `flag' field is nonzero, the value of the option's `val' field
488 if the `flag' field is zero.
489
490 The elements of ARGV aren't really const, because we MONKEY_CRYPTO_random_permute them.
491 But we pretend they're const in the prototype to be compatible
492 with other systems.
493
494 LONGOPTS is a vector of `struct GNoption' terminated by an
495 element containing a name which is zero.
496
497 LONGIND returns the index in LONGOPT of the long-named option found.
498 It is only valid when a long-named option has been found by the most
499 recent call.
500
501 If LONG_ONLY is nonzero, '-' as well as '--' can introduce
502 long-named options. */
503
504static int
505GN_getopt_internal (int argc, char *const *argv, const char *optstring,
506 const struct GNoption *longopts, int *longind,
507 int long_only)
508{
509 static int __getopt_initialized = 0;
510 static int GNopterr = 1;
511
512 GNoptarg = NULL;
513
514 if (GNoptind == 0 || !__getopt_initialized)
515 {
516 if (GNoptind == 0)
517 GNoptind = 1; /* Don't scan ARGV[0], the program name. */
518 optstring = _getopt_initialize (argc, argv, optstring);
519 __getopt_initialized = 1;
520 }
521
522 /* Test whether ARGV[GNoptind] points to a non-option argument.
523 * Either it does not have option syntax, or there is an environment flag
524 * from the shell indicating it is not an option. The later information
525 * is only used when the used in the GNU libc. */
526#ifdef _LIBC
527#define NONOPTION_P (argv[GNoptind][0] != '-' || argv[GNoptind][1] == '\0' \
528 || (GNoptind < nonoption_flags_len \
529 && __getopt_nonoption_flags[GNoptind] == '1'))
530#else
531#define NONOPTION_P (argv[GNoptind][0] != '-' || argv[GNoptind][1] == '\0')
532#endif
533
534 if (nextchar == NULL || *nextchar == '\0')
535 {
536 /* Advance to the next ARGV-element. */
537
538 /* Give FIRST_NONOPT & LAST_NONOPT rational values if GNoptind has been
539 * moved back by the user (who may also have changed the arguments). */
540 if (last_nonopt > GNoptind)
541 last_nonopt = GNoptind;
542 if (first_nonopt > GNoptind)
543 first_nonopt = GNoptind;
544
545 if (ordering == PERMUTE)
546 {
547 /* If we have just processed some options following some non-options,
548 * exchange them so that the options come first. */
549
550 if (first_nonopt != last_nonopt && last_nonopt != GNoptind)
551 exchange ((char **) argv);
552 else if (last_nonopt != GNoptind)
553 first_nonopt = GNoptind;
554
555 /* Skip any additional non-options
556 * and extend the range of non-options previously skipped. */
557
558 while (GNoptind < argc && NONOPTION_P)
559 GNoptind++;
560 last_nonopt = GNoptind;
561 }
562
563 /* The special ARGV-element `--' means premature end of options.
564 * Skip it like a null option,
565 * then exchange with previous non-options as if it were an option,
566 * then skip everything else like a non-option. */
567 if (GNoptind != argc && !strcmp (argv[GNoptind], "--"))
568 {
569 GNoptind++;
570
571 if (first_nonopt != last_nonopt && last_nonopt != GNoptind)
572 exchange ((char **) argv);
573 else if (first_nonopt == last_nonopt)
574 first_nonopt = GNoptind;
575 last_nonopt = argc;
576
577 GNoptind = argc;
578 }
579
580 /* If we have done all the ARGV-elements, stop the scan
581 * and back over any non-options that we skipped and permuted. */
582
583 if (GNoptind == argc)
584 {
585 /* Set the next-arg-index to point at the non-options
586 * that we previously skipped, so the caller will digest them. */
587 if (first_nonopt != last_nonopt)
588 GNoptind = first_nonopt;
589 return -1;
590 }
591
592 /* If we have come to a non-option and did not permute it,
593 * either stop the scan or describe it to the caller and pass it by. */
594
595 if (NONOPTION_P)
596 {
597 if (ordering == REQUIRE_ORDER)
598 return -1;
599 GNoptarg = argv[GNoptind++];
600 return 1;
601 }
602
603 /* We have found another option-ARGV-element.
604 * Skip the initial punctuation. */
605
606 nextchar =
607 (argv[GNoptind] + 1 + (longopts != NULL && argv[GNoptind][1] == '-'));
608 }
609
610 /* Decode the current option-ARGV-element. */
611
612 /* Check whether the ARGV-element is a long option.
613 *
614 * If long_only and the ARGV-element has the form "-f", where f is
615 * a valid short option, don't consider it an abbreviated form of
616 * a long option that starts with f. Otherwise there would be no
617 * way to give the -f short option.
618 *
619 * On the other hand, if there's a long option "fubar" and
620 * the ARGV-element is "-fu", do consider that an abbreviation of
621 * the long option, just like "--fu", and not "-f" with arg "u".
622 *
623 * This distinction seems to be the most useful approach. */
624
625 if (longopts != NULL &&
626 (argv[GNoptind][1] == '-' ||
627 (long_only &&
628 (argv[GNoptind][2] || !my_index (optstring, argv[GNoptind][1])))))
629 {
630 char *nameend;
631 const struct GNoption *p;
632 const struct GNoption *pfound = NULL;
633 int exact = 0;
634 int ambig = 0;
635 int indfound = -1;
636 int option_index;
637
638 for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
639 /* Do nothing. */ ;
640
641 /* Test all long options for either exact match
642 * or abbreviated matches. */
643 for (p = longopts, option_index = 0; p->name; p++, option_index++)
644 if (!strncmp (p->name, nextchar, nameend - nextchar))
645 {
646 if ((unsigned int) (nameend - nextchar) ==
647 (unsigned int) strlen (p->name))
648 {
649 /* Exact match found. */
650 pfound = p;
651 indfound = option_index;
652 exact = 1;
653 break;
654 }
655 else if (pfound == NULL)
656 {
657 /* First nonexact match found. */
658 pfound = p;
659 indfound = option_index;
660 }
661 else
662 /* Second or later nonexact match found. */
663 ambig = 1;
664 }
665
666 if (ambig && !exact)
667 {
668 if (GNopterr)
669 FPRINTF (stderr, _("%s: option `%s' is ambiguous\n"), argv[0],
670 argv[GNoptind]);
671 nextchar += strlen (nextchar);
672 GNoptind++;
673 return '?';
674 }
675
676 if (pfound != NULL)
677 {
678 option_index = indfound;
679 GNoptind++;
680 if (*nameend)
681 {
682 /* Don't test has_arg with >, because some C compilers don't
683 * allow it to be used on enums. */
684 if (pfound->has_arg)
685 GNoptarg = nameend + 1;
686 else
687 {
688 if (GNopterr)
689 {
690 if (argv[GNoptind - 1][1] == '-')
691 /* --option */
692 FPRINTF (stderr,
693 _("%s: option `--%s' does not allow an argument\n"),
694 argv[0], pfound->name);
695 else
696 /* +option or -option */
697 FPRINTF (stderr,
698 _("%s: option `%c%s' does not allow an argument\n"),
699 argv[0], argv[GNoptind - 1][0], pfound->name);
700 }
701 nextchar += strlen (nextchar);
702 return '?';
703 }
704 }
705 else if (pfound->has_arg == 1)
706 {
707 if (GNoptind < argc)
708 {
709 GNoptarg = argv[GNoptind++];
710 }
711 else
712 {
713 if (GNopterr)
714 {
715 FPRINTF (stderr, _("%s: option `%s' requires an argument\n"),
716 argv[0], argv[GNoptind - 1]);
717 }
718 nextchar += strlen (nextchar);
719 return (optstring[0] == ':') ? ':' : '?';
720 }
721 }
722 nextchar += strlen (nextchar);
723 if (longind != NULL)
724 *longind = option_index;
725 if (pfound->flag)
726 {
727 *(pfound->flag) = pfound->val;
728 return 0;
729 }
730 return pfound->val;
731 }
732
733 /* Can't find it as a long option. If this is not getopt_long_only,
734 * or the option starts with '--' or is not a valid short
735 * option, then it's an error.
736 * Otherwise interpret it as a short option. */
737 if (!long_only || argv[GNoptind][1] == '-' ||
738 my_index (optstring, *nextchar) == NULL)
739 {
740 if (GNopterr)
741 {
742 if (argv[GNoptind][1] == '-')
743 /* --option */
744 FPRINTF (stderr, _("%s: unrecognized option `--%s'\n"), argv[0],
745 nextchar);
746 else
747 /* +option or -option */
748 FPRINTF (stderr, _("%s: unrecognized option `%c%s'\n"), argv[0],
749 argv[GNoptind][0], nextchar);
750 }
751 nextchar = (char *) "";
752 GNoptind++;
753 return '?';
754 }
755 }
756
757 /* Look at and handle the next short option-character. */
758
759 {
760 char c = *nextchar++;
761 char *temp = my_index (optstring, c);
762
763 /* Increment `GNoptind' when we start to process its last character. */
764 if (*nextchar == '\0')
765 ++GNoptind;
766
767 if (temp == NULL || c == ':')
768 {
769 if (GNopterr)
770 {
771 if (posixly_correct)
772 /* 1003.2 specifies the format of this message. */
773 FPRINTF (stderr, _("%s: illegal option -- %c\n"), argv[0], c);
774 else
775 FPRINTF (stderr, _("%s: invalid option -- %c\n"), argv[0], c);
776 }
777 return '?';
778 }
779 /* Convenience. Treat POSIX -W foo same as long option --foo */
780 if (temp[0] == 'W' && temp[1] == ';')
781 {
782 char *nameend;
783 const struct GNoption *p;
784 const struct GNoption *pfound = NULL;
785 int exact = 0;
786 int ambig = 0;
787 int indfound = 0;
788 int option_index;
789
790 /* This is an option that requires an argument. */
791 if (*nextchar != '\0')
792 {
793 GNoptarg = nextchar;
794 /* If we end this ARGV-element by taking the rest as an arg,
795 * we must advance to the next element now. */
796 GNoptind++;
797 }
798 else if (GNoptind == argc)
799 {
800 if (GNopterr)
801 {
802 /* 1003.2 specifies the format of this message. */
803 FPRINTF (stderr, _("%s: option requires an argument -- %c\n"),
804 argv[0], c);
805 }
806 if (optstring[0] == ':')
807 c = ':';
808 else
809 c = '?';
810 return c;
811 }
812 else
813 /* We already incremented `GNoptind' once;
814 * increment it again when taking next ARGV-elt as argument. */
815 GNoptarg = argv[GNoptind++];
816
817 /* GNoptarg is now the argument, see if it's in the
818 * table of longopts. */
819
820 for (nextchar = nameend = GNoptarg; *nameend && *nameend != '=';
821 nameend++)
822 /* Do nothing. */ ;
823
824 /* Test all long options for either exact match
825 * or abbreviated matches. */
826 if (longopts != NULL)
827 for (p = longopts, option_index = 0; p->name; p++, option_index++)
828 if (!strncmp (p->name, nextchar, nameend - nextchar))
829 {
830 if ((unsigned int) (nameend - nextchar) == strlen (p->name))
831 {
832 /* Exact match found. */
833 pfound = p;
834 indfound = option_index;
835 exact = 1;
836 break;
837 }
838 else if (pfound == NULL)
839 {
840 /* First nonexact match found. */
841 pfound = p;
842 indfound = option_index;
843 }
844 else
845 /* Second or later nonexact match found. */
846 ambig = 1;
847 }
848 if (ambig && !exact)
849 {
850 if (GNopterr)
851 FPRINTF (stderr, _("%s: option `-W %s' is ambiguous\n"), argv[0],
852 argv[GNoptind]);
853 nextchar += strlen (nextchar);
854 GNoptind++;
855 return '?';
856 }
857 if (pfound != NULL)
858 {
859 option_index = indfound;
860 if (*nameend)
861 {
862 /* Don't test has_arg with >, because some C compilers don't
863 * allow it to be used on enums. */
864 if (pfound->has_arg)
865 GNoptarg = nameend + 1;
866 else
867 {
868 if (GNopterr)
869 FPRINTF (stderr, _("\
870%s: option `-W %s' does not allow an argument\n"), argv[0], pfound->name);
871
872 nextchar += strlen (nextchar);
873 return '?';
874 }
875 }
876 else if (pfound->has_arg == 1)
877 {
878 if (GNoptind < argc)
879 GNoptarg = argv[GNoptind++];
880 else
881 {
882 if (GNopterr)
883 FPRINTF (stderr, _("%s: option `%s' requires an argument\n"),
884 argv[0], argv[GNoptind - 1]);
885 nextchar += strlen (nextchar);
886 return optstring[0] == ':' ? ':' : '?';
887 }
888 }
889 nextchar += strlen (nextchar);
890 if (longind != NULL)
891 *longind = option_index;
892 if (pfound->flag)
893 {
894 *(pfound->flag) = pfound->val;
895 return 0;
896 }
897 return pfound->val;
898 }
899 nextchar = NULL;
900 return 'W'; /* Let the application handle it. */
901 }
902 if (temp[1] == ':')
903 {
904 if (temp[2] == ':')
905 {
906 /* This is an option that accepts an argument optionally. */
907 if (*nextchar != '\0')
908 {
909 GNoptarg = nextchar;
910 GNoptind++;
911 }
912 else
913 GNoptarg = NULL;
914 nextchar = NULL;
915 }
916 else
917 {
918 /* This is an option that requires an argument. */
919 if (*nextchar != '\0')
920 {
921 GNoptarg = nextchar;
922 /* If we end this ARGV-element by taking the rest as an arg,
923 * we must advance to the next element now. */
924 GNoptind++;
925 }
926 else if (GNoptind == argc)
927 {
928 if (GNopterr)
929 {
930 /* 1003.2 specifies the format of this message. */
931 FPRINTF (stderr, _("%s: option requires an argument -- %c\n"),
932 argv[0], c);
933 }
934 if (optstring[0] == ':')
935 c = ':';
936 else
937 c = '?';
938 }
939 else
940 /* We already incremented `GNoptind' once;
941 * increment it again when taking next ARGV-elt as argument. */
942 GNoptarg = argv[GNoptind++];
943 nextchar = NULL;
944 }
945 }
946 return c;
947 }
948}
949
950static int
951GNgetopt_long (int argc, char *const *argv, const char *options,
952 const struct GNoption *long_options, int *opt_index)
953{
954 return GN_getopt_internal (argc, argv, options, long_options, opt_index, 0);
955}
956
957/* ******************** now the GNUnet specific modifications... ********************* */
958
959/**
960 * Parse the command line.
961 *
962 * @param binaryOptions Name of application with option summary
963 * @param allOptions defined options and handlers
964 * @param argc number of arguments
965 * @param argv actual arguments
966 * @return index into argv with first non-option
967 * argument, or -1 on error
968 */
969int
970MONKEY_GETOPT_run (const char *binaryOptions,
971 const struct MONKEY_GETOPT_CommandLineOption *allOptions,
972 unsigned int argc, char *const *argv)
973{
974 struct GNoption *long_options;
975 struct MONKEY_GETOPT_CommandLineProcessorContext clpc;
976 int count;
977 int i;
978 char *shorts;
979 int spos;
980 int cont;
981 int c;
982
983// MONKEY_assert (argc > 0);
984 GNoptind = 0;
985 clpc.binaryName = argv[0];
986 clpc.binaryOptions = binaryOptions;
987 clpc.allOptions = allOptions;
988 clpc.argv = argv;
989 clpc.argc = argc;
990 count = 0;
991 while (allOptions[count].name != NULL)
992 count++;
993 long_options = malloc (sizeof (struct GNoption) * (count + 1));
994 shorts = malloc (count * 2 + 1);
995 spos = 0;
996 for (i = 0; i < count; i++)
997 {
998 long_options[i].name = allOptions[i].name;
999 long_options[i].has_arg = allOptions[i].require_argument;
1000 long_options[i].flag = NULL;
1001 long_options[i].val = allOptions[i].shortName;
1002 shorts[spos++] = allOptions[i].shortName;
1003 if (allOptions[i].require_argument != 0)
1004 shorts[spos++] = ':';
1005 }
1006 long_options[count].name = NULL;
1007 long_options[count].has_arg = 0;
1008 long_options[count].flag = NULL;
1009 long_options[count].val = '\0';
1010 shorts[spos] = '\0';
1011 cont = MONKEY_OK;
1012 /* main getopt loop */
1013 while (cont == MONKEY_OK)
1014 {
1015 int option_index = 0;
1016
1017 c = GNgetopt_long (argc, argv, shorts, long_options, &option_index);
1018
1019 if (c == MONKEY_SYSERR)
1020 break; /* No more flags to process */
1021
1022 for (i = 0; i < count; i++)
1023 {
1024 clpc.currentArgument = GNoptind - 1;
1025 if ((char) c == allOptions[i].shortName)
1026 {
1027 cont =
1028 allOptions[i].processor (&clpc, allOptions[i].scls,
1029 allOptions[i].name, GNoptarg);
1030 break;
1031 }
1032 }
1033 if (i == count)
1034 {
1035 FPRINTF (stderr, _("Use %s to get a list of options.\n"), "--help");
1036 cont = MONKEY_SYSERR;
1037 }
1038 }
1039
1040 free (shorts);
1041 free (long_options);
1042 if (cont == MONKEY_SYSERR)
1043 return MONKEY_SYSERR;
1044 return GNoptind;
1045}
1046
1047/* end of getopt.c */
diff --git a/pathologist/src/pathologist/getopt_helpers.c b/pathologist/src/pathologist/getopt_helpers.c
new file mode 100644
index 0000000..50d02d8
--- /dev/null
+++ b/pathologist/src/pathologist/getopt_helpers.c
@@ -0,0 +1,294 @@
1/*
2 This file is part of GNUnet
3 (C) 2006, 2011 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/util/getopt_helpers.c
23 * @brief implements command line that sets option
24 * @author Christian Grothoff
25 */
26
27#include <string.h>
28#include "platform.h"
29#include "monkey_common.h"
30#include "monkey_getopt_lib.h"
31
32#define LOG(kind,...) MONKEY_log_from (kind, "util", __VA_ARGS__)
33
34
35/**
36 * Print out program version (implements --version).
37 *
38 * @param ctx command line processing context
39 * @param scls additional closure (points to version string)
40 * @param option name of the option
41 * @param value not used (NULL)
42 * @return MONKEY_SYSERR (do not continue)
43 */
44int
45MONKEY_GETOPT_print_version_ (struct MONKEY_GETOPT_CommandLineProcessorContext
46 *ctx, void *scls, const char *option,
47 const char *value)
48{
49 const char *version = scls;
50
51 printf ("%s v%s\n", ctx->binaryName, version);
52 return MONKEY_SYSERR;
53}
54
55
56
57#define BORDER 29
58
59/**
60 * Print out details on command line options (implements --help).
61 *
62 * @param ctx command line processing context
63 * @param scls additional closure (points to about text)
64 * @param option name of the option
65 * @param value not used (NULL)
66 * @return MONKEY_SYSERR (do not continue)
67 */
68int
69MONKEY_GETOPT_format_help_ (struct MONKEY_GETOPT_CommandLineProcessorContext
70 *ctx, void *scls, const char *option,
71 const char *value)
72{
73 const char *about = scls;
74 size_t slen;
75 unsigned int i;
76 int j;
77 size_t ml;
78 size_t p;
79 char *scp;
80 const char *trans;
81 const struct MONKEY_GETOPT_CommandLineOption *opt;
82
83 if (NULL != about)
84 {
85 printf ("%s\n%s\n", ctx->binaryOptions, about);
86 printf ("Arguments mandatory for long options are also mandatory for short options.\n");
87 }
88 i = 0;
89 opt = ctx->allOptions;
90 while (opt[i].description != NULL)
91 {
92 if (opt[i].shortName == '\0')
93 printf (" ");
94 else
95 printf (" -%c, ", opt[i].shortName);
96 printf ("--%s", opt[i].name);
97 slen = 8 + strlen (opt[i].name);
98 if (opt[i].argumentHelp != NULL)
99 {
100 printf ("=%s", opt[i].argumentHelp);
101 slen += 1 + strlen (opt[i].argumentHelp);
102 }
103 if (slen > BORDER)
104 {
105 printf ("\n%*s", BORDER, "");
106 slen = BORDER;
107 }
108 if (slen < BORDER)
109 {
110 printf ("%*s", (int) (BORDER - slen), "");
111 slen = BORDER;
112 }
113 if (0 < strlen (opt[i].description))
114 trans = opt[i].description;
115 else
116 trans = "";
117 ml = strlen (trans);
118 p = 0;
119OUTER:
120 while (ml - p > 78 - slen)
121 {
122 for (j = p + 78 - slen; j > p; j--)
123 {
124 if (isspace ((unsigned char) trans[j]))
125 {
126 scp = malloc (j - p + 1);
127 memcpy (scp, &trans[p], j - p);
128 scp[j - p] = '\0';
129 printf ("%s\n%*s", scp, BORDER + 2, "");
130 free (scp);
131 p = j + 1;
132 slen = BORDER + 2;
133 goto OUTER;
134 }
135 }
136 /* could not find space to break line */
137 scp = malloc (78 - slen + 1);
138 memcpy (scp, &trans[p], 78 - slen);
139 scp[78 - slen] = '\0';
140 printf ("%s\n%*s", scp, BORDER + 2, "");
141 free (scp);
142 slen = BORDER + 2;
143 p = p + 78 - slen;
144 }
145 /* print rest */
146 if (p < ml)
147 printf ("%s\n", &trans[p]);
148 if (strlen (trans) == 0)
149 printf ("\n");
150 i++;
151 }
152 printf ("Report bugs to gnunet-developers@gnu.org.\n"
153 "GNUnet home page: http://www.gnu.org/software/gnunet/\n"
154 "General help using GNU software: http://www.gnu.org/gethelp/\n");
155 return MONKEY_SYSERR;
156}
157
158
159/**
160 * Set an option of type 'unsigned int' from the command line. Each
161 * time the option flag is given, the value is incremented by one.
162 * A pointer to this function should be passed as part of the
163 * 'struct MONKEY_GETOPT_CommandLineOption' array to initialize options
164 * of this type. It should be followed by a pointer to a value of
165 * type 'int'.
166 *
167 * @param ctx command line processing context
168 * @param scls additional closure (will point to the 'int')
169 * @param option name of the option
170 * @param value not used (NULL)
171 * @return MONKEY_OK
172 */
173int
174MONKEY_GETOPT_increment_value (struct MONKEY_GETOPT_CommandLineProcessorContext
175 *ctx, void *scls, const char *option,
176 const char *value)
177{
178 int *val = scls;
179
180 (*val)++;
181 return MONKEY_OK;
182}
183
184
185/**
186 * Set an option of type 'int' from the command line to 1 if the
187 * given option is present.
188 * A pointer to this function should be passed as part of the
189 * 'struct MONKEY_GETOPT_CommandLineOption' array to initialize options
190 * of this type. It should be followed by a pointer to a value of
191 * type 'int'.
192 *
193 * @param ctx command line processing context
194 * @param scls additional closure (will point to the 'int')
195 * @param option name of the option
196 * @param value not used (NULL)
197 * @return MONKEY_OK
198 */
199int
200MONKEY_GETOPT_set_one (struct MONKEY_GETOPT_CommandLineProcessorContext *ctx,
201 void *scls, const char *option, const char *value)
202{
203 int *val = scls;
204
205 *val = 1;
206 return MONKEY_OK;
207}
208
209
210/**
211 * Set an option of type 'char *' from the command line.
212 * A pointer to this function should be passed as part of the
213 * 'struct MONKEY_GETOPT_CommandLineOption' array to initialize options
214 * of this type. It should be followed by a pointer to a value of
215 * type 'char *'.
216 *
217 * @param ctx command line processing context
218 * @param scls additional closure (will point to the 'char *',
219 * which will be allocated)
220 * @param option name of the option
221 * @param value actual value of the option (a string)
222 * @return MONKEY_OK
223 */
224int
225MONKEY_GETOPT_set_string (struct MONKEY_GETOPT_CommandLineProcessorContext *ctx,
226 void *scls, const char *option, const char *value)
227{
228 char **val = scls;
229
230// MONKEY_assert (value != NULL);
231// MONKEY_free_non_null (*val);
232 if(*val != NULL) free(*val);
233 *val = strdup (value);
234 return MONKEY_OK;
235}
236
237
238/**
239 * Set an option of type 'unsigned long long' from the command line.
240 * A pointer to this function should be passed as part of the
241 * 'struct MONKEY_GETOPT_CommandLineOption' array to initialize options
242 * of this type. It should be followed by a pointer to a value of
243 * type 'unsigned long long'.
244 *
245 * @param ctx command line processing context
246 * @param scls additional closure (will point to the 'unsigned long long')
247 * @param option name of the option
248 * @param value actual value of the option as a string.
249 * @return MONKEY_OK if parsing the value worked
250 */
251int
252MONKEY_GETOPT_set_ulong (struct MONKEY_GETOPT_CommandLineProcessorContext *ctx,
253 void *scls, const char *option, const char *value)
254{
255 unsigned long long *val = scls;
256
257 if (1 != SSCANF (value, "%llu", val))
258 {
259 FPRINTF (stderr, "You must pass a number to the `%s' option.\n", option);
260 return MONKEY_SYSERR;
261 }
262 return MONKEY_OK;
263}
264
265
266/**
267 * Set an option of type 'unsigned int' from the command line.
268 * A pointer to this function should be passed as part of the
269 * 'struct MONKEY_GETOPT_CommandLineOption' array to initialize options
270 * of this type. It should be followed by a pointer to a value of
271 * type 'unsigned int'.
272 *
273 * @param ctx command line processing context
274 * @param scls additional closure (will point to the 'unsigned int')
275 * @param option name of the option
276 * @param value actual value of the option as a string.
277 * @return MONKEY_OK if parsing the value worked
278 */
279int
280MONKEY_GETOPT_set_uint (struct MONKEY_GETOPT_CommandLineProcessorContext *ctx,
281 void *scls, const char *option, const char *value)
282{
283 unsigned int *val = scls;
284
285 if (1 != SSCANF (value, "%u", val))
286 {
287 FPRINTF (stderr, "You must pass a number to the `%s' option.\n", option);
288 return MONKEY_SYSERR;
289 }
290 return MONKEY_OK;
291}
292
293
294/* end of getopt_helpers.c */
diff --git a/pathologist/src/pathologist/mail_sender.c b/pathologist/src/pathologist/mail_sender.c
new file mode 100644
index 0000000..98edf84
--- /dev/null
+++ b/pathologist/src/pathologist/mail_sender.c
@@ -0,0 +1,231 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <ctype.h>
4#include <unistd.h>
5#include <getopt.h>
6#include <string.h>
7#include <fcntl.h>
8#include <signal.h>
9#include <errno.h>
10#include <stdarg.h>
11
12#include <openssl/ssl.h>
13#include <auth-client.h>
14#include <libesmtp.h>
15
16#if !defined (__GNUC__) || __GNUC__ < 2
17# define __attribute__(x)
18#endif
19
20
21int
22handle_invalid_peer_certificate(long vfy_result)
23{
24 const char *k ="rare error";
25 switch(vfy_result) {
26 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
27 k="X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT"; break;
28 case X509_V_ERR_UNABLE_TO_GET_CRL:
29 k="X509_V_ERR_UNABLE_TO_GET_CRL"; break;
30 case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
31 k="X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE"; break;
32 case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
33 k="X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE"; break;
34 case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
35 k="X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"; break;
36 case X509_V_ERR_CERT_SIGNATURE_FAILURE:
37 k="X509_V_ERR_CERT_SIGNATURE_FAILURE"; break;
38 case X509_V_ERR_CRL_SIGNATURE_FAILURE:
39 k="X509_V_ERR_CRL_SIGNATURE_FAILURE"; break;
40 case X509_V_ERR_CERT_NOT_YET_VALID:
41 k="X509_V_ERR_CERT_NOT_YET_VALID"; break;
42 case X509_V_ERR_CERT_HAS_EXPIRED:
43 k="X509_V_ERR_CERT_HAS_EXPIRED"; break;
44 case X509_V_ERR_CRL_NOT_YET_VALID:
45 k="X509_V_ERR_CRL_NOT_YET_VALID"; break;
46 case X509_V_ERR_CRL_HAS_EXPIRED:
47 k="X509_V_ERR_CRL_HAS_EXPIRED"; break;
48 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
49 k="X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD"; break;
50 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
51 k="X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD"; break;
52 case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
53 k="X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD"; break;
54 case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
55 k="X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD"; break;
56 case X509_V_ERR_OUT_OF_MEM:
57 k="X509_V_ERR_OUT_OF_MEM"; break;
58 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
59 k="X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"; break;
60 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
61 k="X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN"; break;
62 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
63 k="X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"; break;
64 case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
65 k="X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"; break;
66 case X509_V_ERR_CERT_CHAIN_TOO_LONG:
67 k="X509_V_ERR_CERT_CHAIN_TOO_LONG"; break;
68 case X509_V_ERR_CERT_REVOKED:
69 k="X509_V_ERR_CERT_REVOKED"; break;
70 case X509_V_ERR_INVALID_CA:
71 k="X509_V_ERR_INVALID_CA"; break;
72 case X509_V_ERR_PATH_LENGTH_EXCEEDED:
73 k="X509_V_ERR_PATH_LENGTH_EXCEEDED"; break;
74 case X509_V_ERR_INVALID_PURPOSE:
75 k="X509_V_ERR_INVALID_PURPOSE"; break;
76 case X509_V_ERR_CERT_UNTRUSTED:
77 k="X509_V_ERR_CERT_UNTRUSTED"; break;
78 case X509_V_ERR_CERT_REJECTED:
79 k="X509_V_ERR_CERT_REJECTED"; break;
80 }
81 printf("SMTP_EV_INVALID_PEER_CERTIFICATE: %ld: %s\n", vfy_result, k);
82 return 1; /* Accept the problem */
83}
84
85
86void event_cb (smtp_session_t session, int event_no, void *arg,...)
87{
88 va_list alist;
89 int *ok;
90
91 va_start(alist, arg);
92 switch(event_no) {
93 case SMTP_EV_CONNECT:
94 case SMTP_EV_MAILSTATUS:
95 case SMTP_EV_RCPTSTATUS:
96 case SMTP_EV_MESSAGEDATA:
97 case SMTP_EV_MESSAGESENT:
98 case SMTP_EV_DISCONNECT: break;
99 case SMTP_EV_WEAK_CIPHER: {
100 int bits;
101 bits = va_arg(alist, long); ok = va_arg(alist, int*);
102 printf("SMTP_EV_WEAK_CIPHER, bits=%d - accepted.\n", bits);
103 *ok = 1; break;
104 }
105 case SMTP_EV_STARTTLS_OK:
106 puts("SMTP_EV_STARTTLS_OK - TLS started here."); break;
107 case SMTP_EV_INVALID_PEER_CERTIFICATE: {
108 long vfy_result;
109 vfy_result = va_arg(alist, long); ok = va_arg(alist, int*);
110 *ok = handle_invalid_peer_certificate(vfy_result);
111 break;
112 }
113 case SMTP_EV_NO_PEER_CERTIFICATE: {
114 ok = va_arg(alist, int*);
115 puts("SMTP_EV_NO_PEER_CERTIFICATE - accepted.");
116 *ok = 1; break;
117 }
118 case SMTP_EV_WRONG_PEER_CERTIFICATE: {
119 ok = va_arg(alist, int*);
120 puts("SMTP_EV_WRONG_PEER_CERTIFICATE - accepted.");
121 *ok = 1; break;
122 }
123 case SMTP_EV_NO_CLIENT_CERTIFICATE: {
124 ok = va_arg(alist, int*);
125 puts("SMTP_EV_NO_CLIENT_CERTIFICATE - accepted.");
126 *ok = 1; break;
127 }
128 default:
129 printf("Got event: %d - ignored.\n", event_no);
130 }
131 va_end(alist);
132}
133
134
135/* Callback to prnt the recipient status */
136void
137print_recipient_status (smtp_recipient_t recipient,
138 const char *mailbox, void *arg)
139{
140 const smtp_status_t *status;
141
142 status = smtp_recipient_status (recipient);
143 printf ("%s: %d %s", mailbox, status->code, status->text);
144}
145
146
147void sendMail(const char *messageContents, const char* reportFileName, const char *emailAddress)
148{
149 smtp_session_t session;
150 smtp_message_t message;
151 smtp_recipient_t recipient;
152 const smtp_status_t *status;
153 struct sigaction sa;
154 char *host = "localhost:25";
155 char *from = "monkey@gnunet.org";
156 char *subject = "Monkey bug report";
157 const char *recipient_address = emailAddress;
158 char tempFileName[1000];
159 int tempFd;
160 FILE *fp;
161 enum notify_flags notify = Notify_SUCCESS | Notify_FAILURE;
162
163 auth_client_init();
164 session = smtp_create_session();
165 message = smtp_add_message(session);
166
167 /* Ignore sigpipe */
168 sa.sa_handler = SIG_IGN;
169 sigemptyset(&sa.sa_mask);
170 sa.sa_flags = 0;
171 sigaction(SIGPIPE, &sa, NULL);
172
173
174 smtp_set_server(session, host);
175 smtp_set_eventcb(session, event_cb, NULL);
176
177 /* Set the reverse path for the mail envelope. (NULL is ok)
178 */
179 smtp_set_reverse_path(message, from);
180
181 /* Set the Subject: header. For no reason, we want the supplied subject
182 to override any subject line in the message headers. */
183 if (subject != NULL) {
184 smtp_set_header(message, "Subject", subject);
185 smtp_set_header_option(message, "Subject", Hdr_OVERRIDE, 1);
186 }
187
188
189 /* Prepare message */
190 if (NULL == reportFileName) {
191 memset(tempFileName, 0, sizeof(tempFileName));
192 sprintf(tempFileName, "/tmp/messageXXXXXX");
193 tempFd = mkstemp(tempFileName);
194 fp = fdopen(tempFd, "w");
195 fprintf(fp, "%s", messageContents);
196 fclose(fp);
197 fp = fopen(tempFileName, "r");
198 smtp_set_message_fp(message, fp);
199 } else {
200 fp = fopen(reportFileName, "r");
201 smtp_set_message_fp(message, fp);
202 }
203
204
205 recipient = smtp_add_recipient(message, recipient_address);
206
207 smtp_dsn_set_notify (recipient, notify);
208
209 /* Initiate a connection to the SMTP server and transfer the
210 message. */
211 if (!smtp_start_session(session)) {
212 char buf[128];
213
214 fprintf(stderr, "SMTP server problem %s\n", smtp_strerror(smtp_errno(),
215 buf, sizeof buf));
216 } else {
217 /* Report on the success or otherwise of the mail transfer.
218 */
219 status = smtp_message_transfer_status(message);
220 printf("%d %s", status->code, (status->text != NULL) ? status->text
221 : "\n");
222 smtp_enumerate_recipients(message, print_recipient_status, NULL);
223 }
224
225 /* Free resources consumed by the program.
226 */
227 smtp_destroy_session(session);
228 fclose(fp);
229 auth_client_exit();
230}
231
diff --git a/pathologist/src/pathologist/pathologist.c b/pathologist/src/pathologist/pathologist.c
new file mode 100644
index 0000000..305c3ce
--- /dev/null
+++ b/pathologist/src/pathologist/pathologist.c
@@ -0,0 +1,357 @@
1/*
2 This file is part of GNUnet.
3 (C) 2010, 2011 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file pathologist/pathologist.c
23 * @brief Monkey Pathologist: automated debugging tool
24 */
25
26#include <stdio.h>
27#include <string.h>
28#include <unistd.h>
29#include <sys/stat.h>
30#include "monkey_common.h"
31#include "action.h"
32#include "monkey_getopt_lib.h"
33
34static char *dumpFileName;
35static char *binaryName;
36static char *command;
37static char *binaryArgs;
38static char *emailAddress;
39static char *edbFilePath;
40static char *gdbBinaryPath;
41static char *valgrindBinaryPath;
42static char *inspectExpression;
43static char *inspectFunction;
44static int scopeDepth;
45static int reverseExecutionAllowed = MONKEY_NO;
46static int ret = 0;
47
48static int checkFileExistence(const char *filePath) {
49 struct stat statBuff;
50 if (0 == stat(filePath, &statBuff)) {
51 return MONKEY_YES;
52 }
53 return MONKEY_NO;
54}
55
56
57static int checkELF(const char *binaryPath) {
58 int ch;
59 const char ELF[5] = {0x7f, 'E', 'L', 'F', '\0'}; // ELF magic number
60 char inputArr[5];
61 FILE *filePtr;
62 filePtr = fopen(binaryPath, "r");
63 int i;
64
65 for (i = 0; i < 4; i++) {
66 ch = fgetc(filePtr);
67 if (EOF == ch) {
68 return MONKEY_NO;
69 }
70 inputArr[i] = ch;
71 }
72 inputArr[4] = '\0';
73 if (strcmp(ELF, inputArr) == 0) {
74 return MONKEY_YES;
75 }
76 return MONKEY_NO;
77}
78/**
79 * Main function that will launch Pathologist's action api.
80*/
81static void run()
82{
83 int result;
84 struct MONKEY_ACTION_Context *cntxt;
85
86 /* Check files existence */
87 if (MONKEY_NO == checkFileExistence(binaryName)) {
88 fprintf(stderr, "Error: Target binary file does not exist.\n");
89 ret = 1;
90 return;
91 }
92
93 if (MONKEY_NO == checkFileExistence(edbFilePath)) {
94 fprintf(stderr, "Error: Expression database file does not exist.\n");
95 ret = 1;
96 return;
97 }
98
99 if (NULL != gdbBinaryPath &&
100 MONKEY_NO == checkFileExistence(gdbBinaryPath))
101 {
102 fprintf(stderr, "Error: gdb binary file does not exist.\n");
103 ret = 1;
104 return;
105
106 }
107
108 if (NULL != valgrindBinaryPath &&
109 MONKEY_NO == checkFileExistence(valgrindBinaryPath))
110 {
111 fprintf(stderr, "Error: valgrind binary file does not exist.\n");
112 ret = 1;
113 return;
114 }
115
116 /* Check if the target program is and ELF binary */
117 if (MONKEY_NO == checkELF(binaryName)) {
118 FILE* targetProgramPipe;
119 char buffer[128];
120 ret = 1;
121
122 fprintf(stderr, "Target program is not an ELF. Pathologist will not debug the program.\n");
123 fprintf(stderr, "Pathologist will attempt to run the program normally.\n");
124
125 targetProgramPipe = popen(command, "r");
126 if (NULL == targetProgramPipe) {
127 fprintf(stderr, "Target is not a binary.\n");
128 return;
129 }
130 while(!feof(targetProgramPipe)) {
131 if (NULL != fgets(buffer, 128, targetProgramPipe)) {
132 fputs(buffer, stderr);
133 }
134 }
135 pclose(targetProgramPipe);
136 return;
137 }
138
139 if (NULL == edbFilePath)
140 {
141 /* lookup a file named test.db in the working directory */
142 struct stat buf;
143 if (0 != stat ("test.db", &buf))
144 {
145 fprintf( stderr,
146 "Expression Database file was not provided as and argument, "
147 "and there is no file named test.db in pathologist's working directory!\n");
148 MONKEY_break (0);
149 ret = 1;
150 return;
151 }
152 }
153
154 if (NULL == dumpFileName)
155 {
156 /* if no file name provided for the output report, the default report name will be binaryName_PIDpid */
157 const char *bn = strrchr(binaryName, '/');
158 if (NULL == bn) {
159 bn = binaryName;
160 } else {
161 bn++;
162 }
163 MONKEY_asprintf(&dumpFileName, "%s_PID%d", bn, getpid());
164 }
165
166 /* Initialize context for the Action API */
167 cntxt = MONKEY_malloc (sizeof (struct MONKEY_ACTION_Context));
168 cntxt->email_address = emailAddress;
169 cntxt->binary_name = binaryName;
170 cntxt->binaryArgs = binaryArgs;
171 cntxt->expression_database_path = edbFilePath;
172 cntxt->gdb_binary_path = gdbBinaryPath;
173 cntxt->valgrind_binary_path = valgrindBinaryPath;
174 cntxt->inspect_expression = inspectExpression;
175 cntxt->inspect_function = inspectFunction;
176 cntxt->scope_depth = scopeDepth;
177 cntxt->bug_detected = NO_BUG_DETECTED;
178 cntxt->has_null = MONKEY_NO;
179 cntxt->xml_report_node = NULL;
180 cntxt->run_reverse = reverseExecutionAllowed;
181 cntxt->gdb_connected = MONKEY_NO;
182
183 result = MONKEY_ACTION_rerun_with_gdb (cntxt);
184 int retVal;
185 switch (result)
186 {
187 case MONKEY_NO:
188 case GDB_STATE_ERROR:
189 fprintf (stderr, "Error in using gdb. Pathologist will exit now!\n");
190 ret = 1;
191 break;
192 case GDB_STATE_EXIT_NORMALLY:
193 fprintf (stderr, "Debug with gdb, program exited normally!\n");
194 break;
195 case GDB_STATE_STOPPED:
196 //categorize bug
197 if ((strcasecmp(cntxt->gdb_stop_reason->signal_meaning, "Segmentation fault") == 0)
198 || (strcasecmp(cntxt->gdb_stop_reason->signal_meaning, "Signal 0") == 0))
199 cntxt->bug_detected = MONKEY_ACTION_is_Nullpointer(cntxt) ? BUG_NULL_POINTER : BUG_BAD_MEM_ACCESS;
200 else if (strcasecmp(cntxt->gdb_stop_reason->signal_meaning, "Aborted") == 0)
201 cntxt->bug_detected = BUG_ABORT;
202 else if (strcasecmp(cntxt->gdb_stop_reason->signal_meaning, "Arithmetic exception") == 0)
203 cntxt->bug_detected = BUG_ARITHMETIC;
204 else if (strcasecmp(cntxt->gdb_stop_reason->signal_name, "SIGBUS") == 0)
205 cntxt->bug_detected = BUG_SIG_BUS;
206
207 // get relevant expressions
208 retVal = MONKEY_ACTION_inspect_expression_database (cntxt);
209
210 if (MONKEY_NO == retVal)
211 {
212 fprintf( stderr,
213 "Error in using the expression database. Pathologist will exit now!\n");
214 ret = 1;
215 break;
216 }
217 else if (BUG_BAD_MEM_ACCESS == cntxt->bug_detected
218 || BUG_SIG_BUS == cntxt->bug_detected)
219 {
220 /* launch valgrind */
221 retVal = MONKEY_ACTION_rerun_with_valgrind (cntxt);
222 if (MONKEY_NO == retVal)
223 fprintf( stderr, "Error using Valgrind! Pathologist will continue debugging!\n");
224 }
225
226 /* Start backtracking if enabled by user */
227 if (cntxt->run_reverse) {
228 retVal = MONKEY_ACTION_start_reverse_execution(cntxt);
229 if (MONKEY_NO == retVal)
230 fprintf(stderr, "Error during Backtracking! Pathologist will continue debugging with backtracking.\n");
231 }
232
233
234 // REPORTING
235 if (MONKEY_OK != MONKEY_ACTION_format_report_xml (cntxt))
236 {
237 fprintf( stderr,
238 "Error in generating debug report!\n");
239 ret = 1;
240 }
241 if (emailAddress != NULL)
242 {
243 if (MONKEY_OK != MONKEY_ACTION_report_email (cntxt, dumpFileName))
244 {
245 fprintf( stderr, "Error sending email!\n");
246 ret = 1;
247 }
248 }
249 if (dumpFileName != NULL)
250 {
251 if (MONKEY_OK !=
252 MONKEY_ACTION_report_file (cntxt, dumpFileName, MONKEY_YES))
253 {
254 fprintf( stderr,
255 "Error in saving debug file!\n");
256 ret = 1;
257 }
258 }
259 break;
260 default:
261 break;
262 }
263
264MONKEY_ACTION_delete_context (cntxt);
265}
266
267
268int freePathologistArgs()
269{
270 if (NULL != dumpFileName)
271 MONKEY_free(dumpFileName);
272 if (NULL != emailAddress)
273 MONKEY_free(emailAddress);
274 if (NULL != edbFilePath)
275 MONKEY_free(edbFilePath);
276 if (NULL != gdbBinaryPath)
277 MONKEY_free(gdbBinaryPath);
278 if (NULL != valgrindBinaryPath)
279 MONKEY_free(valgrindBinaryPath);
280 if (NULL != inspectExpression)
281 MONKEY_free(inspectExpression);
282 if (NULL != inspectFunction)
283 MONKEY_free(inspectFunction);
284
285 return MONKEY_OK;
286}
287
288
289
290const char * buildBinaryArgs(int argc, char **argv) {
291 char * res = MONKEY_strdup("");
292 char * tmp;
293 int i;
294 for (i = 0; i < argc; i++) {
295 MONKEY_asprintf(&tmp, "%s %s", res, argv[i]);
296 MONKEY_free(res);
297 res = tmp;
298 }
299
300 return res;
301}
302
303int
304main (int argc, char *argv[])
305{
306 static const struct MONKEY_GETOPT_CommandLineOption options[] = {
307 {'d', "database", "FILENAME", "the expression database to use, generated by seaspider",
308 1, &MONKEY_GETOPT_set_string, &edbFilePath},
309 {'f', "function", "FUNCTIONNAME", "the name of the function, in which pathologist will set a breakpoint",
310 1, &MONKEY_GETOPT_set_string, &inspectFunction},
311 {'g', "gdb", "FILENAME", "path to gdb binary to use. If not passed, pathologist will set it to /usr/bin/gdb",
312 1, &MONKEY_GETOPT_set_string, &gdbBinaryPath},
313 {'h', "help", NULL, "shows this help",
314 0, &MONKEY_GETOPT_format_help_, "Monkey Pathologist: Automatically debug a service"},
315 {'i', "inspect", "EXPRESSIONNAME", "expression to inspect in the function specified after argument f",
316 1, &MONKEY_GETOPT_set_string, &inspectExpression},
317 {'l', "valgrind", "FILENAME", "path to valgrind binary. If not passed, pathologist will set it to /usr/bin/valgrind",
318 1, &MONKEY_GETOPT_set_string, &valgrindBinaryPath},
319 {'m', "mail", "MAILADDRESS", "if specified, pathologist will send the bugreport to the given address",
320 1, &MONKEY_GETOPT_set_string, &emailAddress},
321 {'o', "report", "FILENAME", "saves the xml formated report to the given file",
322 1, &MONKEY_GETOPT_set_string, &dumpFileName},
323 {'p', "depth", "UINT", "scope depth. How many scopes should pathologist lookup expression values outer than the scope in which the problem occurs. Default is 0 which means, pathologist is restricted to the scope of the expression in which the debugged program stopped" ,
324 1, &MONKEY_GETOPT_set_uint, &scopeDepth},
325 {'r', "reverse", NULL, "enables reverse execution",
326 0, &MONKEY_GETOPT_set_one, &reverseExecutionAllowed},
327 {'v', "version", NULL, "print the version number",
328 0, &MONKEY_GETOPT_print_version_, "0.1 alpha"},
329 MONKEY_GETOPT_OPTION_END
330 };
331
332 if (-1 == (ret = MONKEY_GETOPT_run ("pathologist -d DATABASEFILE [options] [--] BINARY", options, (unsigned int) argc, argv)))
333 return -1;
334
335 // Check for required arguments
336 if (argc <= ret)
337 printf("Error: missing argument: path to the binary file.\n");
338 if (NULL == edbFilePath)
339 printf("Error: missing argument: path to expression database file.\n");
340 if (argc <= ret || NULL == edbFilePath)
341 exit(EXIT_FAILURE);
342
343 /* All remaining args are considered program arguments */
344 binaryName = argv[ret];
345 command = (char *)buildBinaryArgs(argc - ret, &argv[ret]) + 1;
346 binaryArgs = (char *)buildBinaryArgs(argc - ret - 1, &argv[ret + 1]);
347
348 printf("\n%s\n%s\n%s\n\n", binaryName, command, binaryArgs);
349
350 /* Run Pathologist */
351 run();
352
353 /* clean up */
354 freePathologistArgs();
355
356 return ret;
357}
diff --git a/pathologist/src/pathologist/pathologist_edb.h b/pathologist/src/pathologist/pathologist_edb.h
new file mode 100644
index 0000000..33e18ee
--- /dev/null
+++ b/pathologist/src/pathologist/pathologist_edb.h
@@ -0,0 +1,158 @@
1/**
2 * @file monkey/MONKEY_edb.h
3 * @brief Monkey API for accessing the Expression Database (edb)
4 */
5
6#ifndef MONKEY_EDB_H
7#define MONKEY_EDB_H
8
9#ifdef __cplusplus
10extern "C"
11{
12#if 0 /* keep Emacsens' auto-indent happy */
13}
14#endif
15#endif
16
17
18struct MONKEY_EDB_Context;
19
20/**
21 * Establish a connection to the Expression Database
22 *
23 * @param db_file_name path the Expression Database file
24 * @return context to use for Accessing the Expression Database, NULL on error
25 */
26struct MONKEY_EDB_Context *MONKEY_EDB_connect (const char
27 *db_file_name);
28
29
30/**
31 * Disconnect from Database, and cleanup resources
32 *
33 * @param context context
34 * @return MONKEY_OK on success, MONKEY_NO on failure
35 */
36int MONKEY_EDB_disconnect (struct MONKEY_EDB_Context *cntxt);
37
38
39typedef int (*MONKEY_ExpressionIterator) (void *, int, char **,
40 char **);
41
42typedef int (*MONKEY_FileIterator) (void *, int, char **, char **);
43
44
45
46int
47MONKEY_EDB_get_all_outer_scopes(struct MONKEY_EDB_Context
48 *cntxt, const char *file_name,
49 int function_beginning,
50 int scope_in_question_start,
51 int scope_in_question_end,
52 MONKEY_ExpressionIterator
53 iter, void *iter_cls);
54
55int
56MONKEY_EDB_expression_function_end_scope(struct MONKEY_EDB_Context
57 *cntxt, const char *file_name,
58 int start_lineno,
59 MONKEY_ExpressionIterator
60 iter, void *iter_cls);
61int
62MONKEY_EDB_function_start_line_for_scope(struct MONKEY_EDB_Context
63 *cntxt, const char *file_name,
64 int scope_end,
65 MONKEY_ExpressionIterator
66 iter, void *iter_cls);
67/**
68 * Return the line number of the end-of-scope for the expression indicated by start_line_no
69 *
70 * @param cntxt context containing the Expression Database handle
71 * @param file_name path to the file in which the expression in question exists
72 * @param start_line_no expression's line
73 * @param iter callback function, iterator for values returned from the Database
74 * @param iter_cls closure for the expression iterator, will contain the scope-end line number
75 * @return MONKEY_OK on success, MONKEY_NO on failure
76 */
77int
78MONKEY_EDB_get_expression_scope_end (struct MONKEY_EDB_Context
79 *cntxt, const char *file_name,
80 int start_line_no,
81 MONKEY_ExpressionIterator
82 iter, void *iter_cls);
83
84
85int
86MONKEY_EDB_get_expression_scope_end_for_expression (struct MONKEY_EDB_Context
87 *cntxt, const char *file_name,
88 const char * expr_syntax,
89 MONKEY_ExpressionIterator
90 iter, void *iter_cls);
91
92/**
93 * Return all the source code file names available in the Expression Database
94 *
95 * @param cntxt context containing the Expression Database handle
96 * @param iter callback function, iterator for values returned from the Database
97 * @param iter_cls closure for the file names iterator, will contain the file names
98 * @return MONKEY_OK on success, MONKEY_NO on failure
99 */
100int
101MONKEY_EDB_get_file_names (struct MONKEY_EDB_Context *cntxt,
102 MONKEY_FileIterator iter,
103 void *iter_cls);
104
105
106int
107MONKEY_EDB_get_globals (struct MONKEY_EDB_Context *cntxt,
108 const char* file_names,
109 MONKEY_ExpressionIterator iter,
110 void *iter_cls);
111
112/**
113 * Run an SQLite query to retrieve those expressions that are previous to
114 * given expression and are in the same scope of the given expression
115 * For example, consider the following code snippet:
116 *
117 * {
118 * struct Something whole; // line no.1
119 * struct SomethingElse part; // line no.2
120 * whole.part = &part; // line no.3
121 * whole.part->member = 1; // line no.4
122 * }
123 *
124 * If the expression supplied to the function is that of line no.4 "whole.part->member = 1;"
125 * The returned list of expressions will be: whole.part (line no.4), whole.part->member (line no.4),
126 * whole (line no.3), whole.part (line no.3), &part (line no.3), whole.part = &part (line no.3)
127 *
128 * @param cntxt context containing the Expression Database handle.
129 * @param file_name path to the file in which the expression in question exists
130 * @param start_line_no expression beginning line
131 * @param iter callback function, iterator for expressions returned from the Database
132 * @param iter_cls closure for the expression iterator
133 * @return MONKEY_OK success, MONKEY_NO failure
134 */
135int
136MONKEY_EDB_get_expressions (struct MONKEY_EDB_Context *cntxt,
137 const char *file_name, int start_line_no,
138 MONKEY_ExpressionIterator iter,
139 void *iter_cls);
140
141
142int
143MONKEY_EDB_get_sub_expressions (struct MONKEY_EDB_Context
144 *cntxt, const char *file_name,
145 int start_line_no, int end_line_no,
146 MONKEY_ExpressionIterator iter,
147 void *iter_cls);
148
149
150
151#if 0 /* keep Emacsens' auto-indent happy */
152{
153#endif
154#ifdef __cplusplus
155}
156#endif
157
158#endif
diff --git a/pathologist/src/pathologist/pathologist_xml_writer.h b/pathologist/src/pathologist/pathologist_xml_writer.h
new file mode 100644
index 0000000..5e74060
--- /dev/null
+++ b/pathologist/src/pathologist/pathologist_xml_writer.h
@@ -0,0 +1,54 @@
1/**
2 * @file monkey/MONKEY_xml_writer.h
3 * @brief Monkey API for generating XML debug report
4 */
5
6
7#ifndef MONKEY_XML_WRITER_H
8#define MONKEY_XML_WRITER_H
9
10#ifdef __cplusplus
11extern "C"
12{
13#if 0 /* keep Emacsens' auto-indent happy */
14}
15#endif
16#endif
17
18
19struct XmlAttribute {
20 struct XmlAttribute *next;
21 struct XmlAttribute *prev;
22 const char *name;
23 const char *value;
24};
25
26
27struct MONKEY_XML_Node {
28 struct MONKEY_XML_Node *next;
29 struct MONKEY_XML_Node *prev;
30 const char *name;
31 const char *value;
32 const char *innerText;
33 struct XmlAttribute* attributeListHead;
34 struct XmlAttribute* attributeListTail;
35 struct MONKEY_XML_Node *childrenListHead;
36 struct MONKEY_XML_Node *childrenListTail;
37};
38
39struct MONKEY_XML_Node* MONKEY_XML_WRITER_new_node(const char *name, const char *innerText);
40int MONKEY_XML_WRITER_delete_tree(struct MONKEY_XML_Node *root);
41struct MONKEY_XML_Node* MONKEY_XML_WRITER_add_child(struct MONKEY_XML_Node *parent, struct MONKEY_XML_Node *child);
42int MONKEY_XML_WRITER_write_document(FILE* file, struct MONKEY_XML_Node *root);
43FILE* MONKEY_XML_WRITER_create_document(const char *filePath);
44int MONKEY_XML_WRITER_add_attribute(struct MONKEY_XML_Node* node, const char *attrName, const char *attrValue);
45int MONKEY_XML_WRITER_add_inner_text(struct MONKEY_XML_Node *node, const char *innerText);
46
47#if 0 /* keep Emacsens' auto-indent happy */
48{
49#endif
50#ifdef __cplusplus
51}
52#endif
53
54#endif
diff --git a/pathologist/src/pathologist/test_monkey_assertion_failure.sh b/pathologist/src/pathologist/test_monkey_assertion_failure.sh
new file mode 100644
index 0000000..2a6d3b0
--- /dev/null
+++ b/pathologist/src/pathologist/test_monkey_assertion_failure.sh
@@ -0,0 +1,16 @@
1#!/bin/sh
2echo -n "Test Monkey with Bug - Assertion Failure -"
3gnunet-monkey -m text -d bug_assertion_failure.db -b bug_assertion_failure -o monkey.out || (echo "Monkey Failed!" && exit 1)
4grep "Bug detected in file:bug_assertion_failure.c" monkey.out > /dev/null || (echo "FAIL" && exit 1)
5grep "function:assertionFailure" monkey.out > /dev/null || (echo "FAIL" && exit 1)
6grep "line:8" monkey.out > /dev/null || (echo "FAIL" && exit 1)
7grep "received signal:SIGABRT" monkey.out > /dev/null || (echo "FAIL" && exit 1)
8grep "Aborted" monkey.out > /dev/null || (echo "FAIL" && exit 1)
9grep "Details:" monkey.out > /dev/null || (echo "FAIL" && exit 1)
10grep "Assertion Failure" monkey.out > /dev/null || (echo "FAIL" && exit 1)
11grep "Expression evaluation:" monkey.out > /dev/null || (echo "FAIL" && exit 1)
12grep "assert(x<4) = Not evaluated" monkey.out > /dev/null || (echo "FAIL" && exit 1)
13grep "x<4 = 0" monkey.out > /dev/null || (echo "FAIL" && exit 1)
14grep "x = 5" monkey.out > /dev/null || (echo "FAIL" && exit 1)
15rm -f monkey.out
16echo "PASS"
diff --git a/pathologist/src/pathologist/test_monkey_bad_memory_access.sh b/pathologist/src/pathologist/test_monkey_bad_memory_access.sh
new file mode 100644
index 0000000..cd86a84
--- /dev/null
+++ b/pathologist/src/pathologist/test_monkey_bad_memory_access.sh
@@ -0,0 +1,15 @@
1echo -n "Test Monkey with Bug - Bad Memory Access -"
2gnunet-monkey -m text -d bug_bad_memory_access.db -b bug_bad_memory_access -o monkey.out || (echo "Monkey Failed!" && exit 1)
3for i in `cat bug_bad_memory_access_rpt.ref`
4do
5 grep $i monkey.out
6 if [ $? -ne 0 ]
7 then
8 {
9 echo $i
10 echo "FAIL"
11 exit 1
12 }
13 fi
14done
15echo "PASS"
diff --git a/pathologist/src/pathologist/test_monkey_data.conf b/pathologist/src/pathologist/test_monkey_data.conf
new file mode 100644
index 0000000..9ae1d67
--- /dev/null
+++ b/pathologist/src/pathologist/test_monkey_data.conf
@@ -0,0 +1,43 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunetd-arm/
3DEFAULTCONFIG = test_arm_api_data.conf
4
5[arm]
6PORT = 23354
7DEFAULTSERVICES =
8BINARY = gnunet-service-arm
9OPTIONS = -L ERROR
10# DEBUG = YES
11#PREFIX = valgrind --tool=memcheck --leak-check=yes
12
13[gnunet-monkey]
14# DEBUG = YES
15PORT = 23355
16BINARY = gnunet-monkey
17# PREFIX = valgrind
18
19
20[fs]
21AUTOSTART = NO
22
23[datastore]
24AUTOSTART = NO
25
26[core]
27AUTOSTART = NO
28
29[transport]
30AUTOSTART = NO
31
32[peerinfo]
33AUTOSTART = NO
34
35[statistics]
36AUTOSTART = YES
37# DEBUG = NO
38
39
40[dns]
41AUTOSTART = NO
42
43
diff --git a/pathologist/src/pathologist/test_monkey_npe.sh b/pathologist/src/pathologist/test_monkey_npe.sh
new file mode 100755
index 0000000..5f3d528
--- /dev/null
+++ b/pathologist/src/pathologist/test_monkey_npe.sh
@@ -0,0 +1,13 @@
1#!/bin/sh
2echo -n "Test Monkey with Bug - Null Pointer Exception -"
3gnunet-monkey -m text -d bug_null_pointer_exception.db -b bug_null_pointer_exception -o npe.out || (echo "Monkey Failed!" && exit 1)
4grep "Bug detected in file:bug_null_pointer_exception.c" npe.out > /dev/null || (echo "FAIL" && exit 1)
5grep "function:crashFunction" npe.out > /dev/null || (echo "FAIL" && exit 1)
6grep "line:14" npe.out > /dev/null || (echo "FAIL" && exit 1)
7grep "reason:(null)" npe.out > /dev/null || (echo "FAIL" && exit 1)
8grep "received signal:SIGSEGV" npe.out > /dev/null || (echo "FAIL" && exit 1)
9grep "Segmentation fault" npe.out > /dev/null || (echo "FAIL" && exit 1)
10grep " Details:" npe.out > /dev/null || (echo "FAIL" && exit 1)
11grep " Expression:crashStruct is NULL" npe.out > /dev/null || (echo "FAIL" && exit 1)
12rm -f npe.out
13echo "PASS"
diff --git a/pathologist/src/pathologist/test_pathologist.c b/pathologist/src/pathologist/test_pathologist.c
new file mode 100644
index 0000000..eac5d2f
--- /dev/null
+++ b/pathologist/src/pathologist/test_pathologist.c
@@ -0,0 +1,412 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include "mxml.h"
6
7#undef DEBUG_ENABLED
8static int OK = 0;
9static int NO = 1;
10static char *reportPath = "../../refs/reports";
11static char *dbPath = "../../refs/db";
12static char *binPath = "../../refs/src";
13
14int compareReports(const char *refFileName, const char *testFileName) {
15 FILE *testFilePtr;
16 FILE *refFilePtr;
17 mxml_node_t *refTree;
18 mxml_node_t *testTree;
19 mxml_node_t *refNode;
20 mxml_node_t *testNode;
21 mxml_node_t *refEpochNode;
22 mxml_node_t *testEpochNode;
23 mxml_node_t *refFunctionNode;
24 mxml_node_t *testFunctionNode;
25 mxml_node_t *refExpressionNode;
26 mxml_node_t *testExpressionNode;
27 const char *refExpressionValue;
28 const char *testExpressionValue;
29
30 refFilePtr = fopen(refFileName, "r");
31 testFilePtr = fopen(testFileName, "r");
32 if (NULL == refFilePtr) {
33 fprintf(stderr, "Test failed: cannot locate reference report: %s.\n", strerror(errno));
34 return NO;
35 }
36 if (NULL == testFilePtr) {
37 fprintf(stderr, "Test failed: cannot locate test report: %s.\n", strerror(errno));
38 return NO;
39 }
40
41 refTree = mxmlLoadFile(NULL, refFilePtr, MXML_TEXT_CALLBACK);
42 testTree = mxmlLoadFile(NULL, testFilePtr, MXML_TEXT_CALLBACK);
43 if (NULL == refTree) {
44 fprintf(stderr, "Test failed: cannot parse reference report.\n");
45 return NO;
46 }
47 if (NULL == testTree) {
48 fprintf(stderr, "Test failed: cannot parse test report.\n");
49 return NO;
50 }
51 fclose(refFilePtr);
52 fclose(testFilePtr);
53
54 refNode = refTree; testNode = testTree; // <?xml version="1.0"?>
55
56 // <crash>...
57 refNode = mxmlWalkNext(mxmlGetFirstChild(refNode), refTree, MXML_DESCEND);
58 testNode = mxmlWalkNext(mxmlGetFirstChild(testNode), testTree, MXML_DESCEND);
59 if (0 != strcmp(mxmlGetElement(refNode), "crash") ||
60 0 != strcmp(mxmlGetElement(testNode), "crash")) {
61 fprintf(stderr, "Test failed: wrong tag. <crash> expected\n");
62 return NO;
63 }
64 if (0 != strcmp(mxmlElementGetAttr(refNode, "category"), mxmlElementGetAttr(testNode, "category")) ||
65 0 != strcmp(mxmlElementGetAttr(refNode, "function"), mxmlElementGetAttr(testNode, "function")) ||
66 0 != strcmp(mxmlElementGetAttr(refNode, "line"), mxmlElementGetAttr(testNode, "line")) ||
67 0 != strcmp(mxmlElementGetAttr(refNode, "file"), mxmlElementGetAttr(testNode, "file"))) {
68 fprintf(stderr, "Test failed: crash attributes mismatch.\n");
69 return NO;
70 }
71
72 // <valgrind> or <history>...
73 refNode = mxmlWalkNext(mxmlGetFirstChild(refNode), refTree, MXML_DESCEND);
74 testNode = mxmlWalkNext(mxmlGetFirstChild(testNode), testTree, MXML_DESCEND);
75 if (0 == strcmp(mxmlGetElement(refNode), "valgrind")) {
76 if (0 != strcmp(mxmlGetElement(testNode), "valgrind")) {
77 fprintf(stderr, "Test failed: wrong tag, <valgrind> expected.\n");
78 return NO;
79 }
80 refNode = mxmlFindElement(refNode, refTree, "history", NULL, NULL, MXML_DESCEND); // move to <history>
81 testNode = mxmlFindElement(testNode, testTree, "history", NULL, NULL, MXML_DESCEND);
82 } else if (0 == strcmp(mxmlGetElement(refNode), "history")) {
83 if (0 != strcmp(mxmlGetElement(testNode), "history")) {
84 fprintf(stderr, "Test failed: wrong tag, <history> expected.\n");
85 return NO;
86 }
87 } else {
88 fprintf(stderr, "Test failed: unknown tag.\n");
89 return NO;
90 }
91
92 // <epoch>'s
93 refEpochNode = mxmlGetFirstChild(refNode);
94 testEpochNode = mxmlGetFirstChild(testNode);
95 while (NULL != refEpochNode && NULL != testEpochNode) {
96 if (0 != strcmp(mxmlGetElement(refEpochNode), "epoch") ||
97 0 != strcmp(mxmlGetElement(testEpochNode), "epoch")) {
98 fprintf(stderr, "Test failed: wrong tag, <epoch> expected.\n");
99 return NO;
100 }
101 // Comparing the epoch steps
102 if (0 != strcmp(mxmlElementGetAttr(refEpochNode, "step"), mxmlElementGetAttr(testEpochNode, "step"))) {
103 fprintf(stderr, "Test failed: epoch steps mismatch.\n");
104 return NO;
105 }
106
107 // <trace>
108 refNode = mxmlWalkNext(mxmlGetFirstChild(refEpochNode), refTree, MXML_DESCEND);
109 testNode = mxmlWalkNext(mxmlGetFirstChild(testEpochNode), testTree, MXML_DESCEND);
110
111 // <function>'s
112 refFunctionNode = mxmlGetFirstChild(refNode);
113 testFunctionNode = mxmlGetFirstChild(testNode);
114 while (NULL != refFunctionNode && NULL != testFunctionNode) {
115 if (0 != strcmp(mxmlGetElement(refFunctionNode), "function") ||
116 0 != strcmp(mxmlGetElement(testFunctionNode), "function")) {
117 fprintf(stderr, "Test failed: wrong tag, <function> expected.\n");
118 return NO;
119 }
120 //comparing function attributes
121 if (0 != strcmp(mxmlElementGetAttr(refFunctionNode, "name"), mxmlElementGetAttr(testFunctionNode, "name")) ||
122 0 != strcmp(mxmlElementGetAttr(refFunctionNode, "line"), mxmlElementGetAttr(testFunctionNode, "line")) ||
123 0 != strcmp(mxmlElementGetAttr(refFunctionNode, "file"), mxmlElementGetAttr(testFunctionNode, "file")) ||
124 0 != strcmp(mxmlElementGetAttr(refFunctionNode, "depth"), mxmlElementGetAttr(testFunctionNode, "depth"))) {
125 fprintf(stderr, "Test failed: function attributes mismatch.\n");
126 return NO;
127 }
128
129 // <expressions>
130 refNode = mxmlWalkNext(mxmlGetFirstChild(refFunctionNode), refTree, MXML_DESCEND);
131 testNode = mxmlWalkNext(mxmlGetFirstChild(testFunctionNode), testTree, MXML_DESCEND);
132
133 // <expression>'s
134 refExpressionNode = mxmlGetFirstChild(refNode);
135 testExpressionNode = mxmlGetFirstChild(testNode);
136 while (NULL != refExpressionNode && NULL != testExpressionNode) {
137 if (0 != strcmp(mxmlGetElement(refExpressionNode), "expression") ||
138 0 != strcmp(mxmlGetElement(testExpressionNode), "expression")) {
139 fprintf(stderr, "Test failed: wrong tag, <expression> expected.\n");
140 return NO;
141 }
142
143 // comparing expression syntax
144 if (0 != strcmp(mxmlElementGetAttr(refExpressionNode, "name"), mxmlElementGetAttr(testExpressionNode, "name"))) {
145 fprintf(stderr, "Test failed: expression syntax mismatch.\n");
146 return NO;
147 }
148
149 // comparing expression values
150 refExpressionValue = mxmlGetText(refExpressionNode, 0);
151 testExpressionValue = mxmlGetText(testExpressionNode, 0);
152 if (0 != strcmp(refExpressionValue, testExpressionValue)) {
153 // different pointer values are accepted
154 if (!(refExpressionValue[0] == '0' && refExpressionValue[1] == 'x'
155 && testExpressionValue[0] == '0' && testExpressionValue[1] == 'x')) {
156 if ((strlen(refExpressionValue) >= 5) || (strlen(testExpressionValue) >= 5)) {
157 // different numeric values longer than 5 digits are accepted (such values are considered garbage in uninitialized variables)
158 char *endPtr = NULL;
159 long refIntVal;
160 long testIntVal;
161 refIntVal = strtol(refExpressionValue, &endPtr, 0); // testing whether the values are numeric
162 testIntVal = strtol(testExpressionValue, &endPtr, 0);
163 if ((0 == refIntVal || 0 == testIntVal) && (ERANGE == errno || EINVAL == errno)) {
164 fprintf(stderr, "Test failed: expression value mismatch.\n");
165 return NO;
166 }
167 } else {
168 // not pointers and not 5+ digits numbers, then mismatch
169 fprintf(stderr, "Test failed: expression value mismatch.\n");
170 return NO;
171 }
172 }
173 }
174
175 // next expression
176 refExpressionNode = mxmlGetNextSibling(mxmlGetNextSibling(refExpressionNode));
177 testExpressionNode = mxmlGetNextSibling(mxmlGetNextSibling(testExpressionNode));
178 }
179 if (NULL != refExpressionNode || NULL != testExpressionNode) {
180 fprintf(stderr, "Test failed: expression mismatch.\n");
181 return NO;
182 }
183
184 // next function
185 refFunctionNode = mxmlGetNextSibling(mxmlGetNextSibling(refFunctionNode));
186 testFunctionNode = mxmlGetNextSibling(mxmlGetNextSibling(testFunctionNode));
187 }
188 if (NULL != refFunctionNode || NULL != testFunctionNode) {
189 fprintf(stderr, "Test failed: function mismatch.\n");
190 return NO;
191 }
192
193 // next epoch step
194 refEpochNode = mxmlGetNextSibling(mxmlGetNextSibling(refEpochNode));
195 testEpochNode = mxmlGetNextSibling(mxmlGetNextSibling(testEpochNode));
196 }
197 if (NULL != refEpochNode || NULL != testEpochNode) {
198 fprintf(stderr, "Test failed: epoch steps mismatch.\n");
199 return NO;
200 }
201
202 mxmlDelete(refTree);
203 mxmlDelete(testTree);
204 return OK;
205}
206
207
208int runTest(const char *refReportName, const char *testReportName,
209 const char *dbName, const char *binaryName, char *commandStr) {
210 FILE *pathologistPipe;
211 char *refReportPath;
212 char *testReportPath;
213 char *dbFilePath;
214 char *binFilePath;
215 char *command;
216 int ret = OK;
217
218 asprintf(&testReportPath, "%s/%s", reportPath, testReportName);
219 asprintf(&dbFilePath, "%s/%s", dbPath, dbName);
220 asprintf(&binFilePath, "%s/%s", binPath, binaryName);
221 asprintf(&command, commandStr, dbFilePath, testReportPath, binFilePath);
222 asprintf(&refReportPath, "%s/%s", reportPath, refReportName);
223
224 // run Monkey pathologist
225 pathologistPipe = popen(command, "r");
226#ifdef DEBUG_ENABLED
227 fprintf(stderr, "Running command: %s\n", command);
228#endif
229 if (NULL == pathologistPipe) {
230 fprintf(stderr, "Fatal error: unable to run Monkey pathologist.\n");
231 ret = NO;
232 }
233 else {
234 char buffer[128];
235 while(!feof(pathologistPipe)) {
236 if(fgets(buffer, 128, pathologistPipe) != NULL) {
237#ifdef DEBUG_ENABLED
238 fputs(buffer, stdout);
239#endif
240 }
241 }
242 pclose(pathologistPipe);
243 // compare results with ref
244 if (NO == compareReports(refReportPath, testReportPath)) {
245 ret = NO;
246 } else {
247 // test passed, delete test report
248 if (0 != remove(testReportPath)) {
249 fprintf(stderr, "Warning: could not remove test file %s\n", testReportPath);
250 }
251 }
252 }
253
254 free(refReportPath); free(command); free(binFilePath); free(dbFilePath); free(testReportPath);
255 return ret;
256}
257
258int main() {
259 int ret = OK;
260 char *commandStr = "pathologist -d %s -o %s %s";
261 char *commandStrReverse = "pathologist -d %s -o %s -r %s";
262 char *commandStrDepthOne = "pathologist -d %s -o %s -p 1 %s";
263 char *commandStrDepthOneReverse = "pathologist -d %s -o %s -p 1 -r %s";
264 char *commandStrDepthThousand = "pathologist -d %s -o %s -p 1000 %s";
265
266 fprintf(stderr, "Testing Monkey Pathologist...\n");
267
268 // Bug Null Pointer Exception
269 fprintf(stderr, "Testing bug null pointer exception...\n");
270 if (NO == runTest("ref_bug_null_pointer_exception.xml",
271 "test_bug_null_pointer_exception.xml", "bug_null_pointer_exception.db",
272 "bug_null_pointer_exception", commandStr)) {
273 ret = NO;
274 fprintf(stderr, "Test bug null pointer exception: FAILED\n");
275 } else {
276 fprintf(stderr, "Test bug null pointer exception: PASSED\n");
277 }
278
279 // Bug Null Pointer Exception with backtracking
280 fprintf(stderr, "Testing bug null pointer exception with backtracking...\n");
281 if (NO == runTest("ref_bt_bug_null_pointer_exception.xml",
282 "test_bt_bug_null_pointer_exception.xml", "bug_null_pointer_exception.db",
283 "bug_null_pointer_exception", commandStrReverse)) {
284 ret = NO;
285 fprintf(stderr, "Test bug null pointer exception with backtracking: FAILED\n");
286 } else {
287 fprintf(stderr, "Test bug null pointer exception with backtracking: PASSED\n");
288 }
289
290 // Bug Null Pointer Exception modified with backtracking
291 fprintf(stderr, "Testing bug null pointer exception modified with backtracking...\n");
292 if (NO == runTest("ref_bt_bug_null_pointer_exception_modified.xml",
293 "test_bt_bug_null_pointer_exception_modified.xml", "bug_null_pointer_exception_modified.db",
294 "bug_null_pointer_exception_modified", commandStrReverse)) {
295 ret = NO;
296 fprintf(stderr, "Test bug null pointer exception modified with backtracking: FAILED\n");
297 } else {
298 fprintf(stderr, "Test bug null pointer exception modified with backtracking: PASSED\n");
299 }
300
301 // Bug Bad Memory Access
302 fprintf(stderr, "Testing bug bad memory access...\n");
303 if (NO == runTest("ref_bug_bad_memory_access.xml",
304 "test_bug_bad_memory_access.xml", "bug_bad_memory_access.db",
305 "bug_bad_memory_access", commandStr)) {
306 ret = NO;
307 fprintf(stderr, "Test bug bad memory access: FAILED\n");
308 } else {
309 fprintf(stderr, "Test bug bad memory access: PASSED\n");
310 }
311
312 // Bug Bad Memory Access with backtracking
313 fprintf(stderr, "Testing bug bad memory access with backtracking...\n");
314 if (NO == runTest("ref_bt_bug_bad_memory_access.xml",
315 "test_bt_bug_bad_memory_access.xml", "bug_bad_memory_access.db",
316 "bug_bad_memory_access", commandStrReverse)) {
317 ret = NO;
318 fprintf(stderr, "Test bug bad memory access with backtracking: FAILED\n");
319 } else {
320 fprintf(stderr, "Test bug bad memory access with backtracking: PASSED\n");
321 }
322
323 // Bug Assertion Failure
324 fprintf(stderr, "Testing bug assertion failure...\n");
325 if (NO == runTest("ref_bug_assertion_failure.xml",
326 "test_bug_assertion_failure.xml", "bug_assertion_failure.db",
327 "bug_assertion_failure", commandStr)) {
328 ret = NO;
329 fprintf(stderr, "Test bug assertion failure: FAILED\n");
330 } else {
331 fprintf(stderr, "Test bug assertion failure: PASSED\n");
332 }
333
334 // Bug Assertion Failure with backtracking
335 fprintf(stderr, "Testing bug assertion failure with backtracking...\n");
336 if (NO == runTest("ref_bt_bug_assertion_failure.xml",
337 "test_bt_bug_assertion_failure.xml", "bug_assertion_failure.db",
338 "bug_assertion_failure", commandStrReverse)) {
339 ret = NO;
340 fprintf(stderr, "Test bug assertion failure with backtracking: FAILED\n");
341 } else {
342 fprintf(stderr, "Test bug assertion failure with backtracking: PASSED\n");
343 }
344
345 // Bug Division By Zero Loop no depth
346 fprintf(stderr, "Testing bug division by zero loop no depth...\n");
347 if (NO == runTest("ref_bug_division_by_zero_nodepth.xml",
348 "test_bug_division_by_zero_nodepth.xml", "bug_division_by_zero_loop.db",
349 "bug_division_by_zero_loop", commandStr)) {
350 ret = NO;
351 fprintf(stderr, "Test bug division by zero loop with no depth: FAILED\n");
352 } else {
353 fprintf(stderr, "Test bug division by zero loop with no depth: PASSED\n");
354 }
355
356 // Bug Division By Zero Loop no depth with backtracking
357 fprintf(stderr, "Testing bug division by zero loop no depth with backtracking...\n");
358 if (NO == runTest("ref_bt_bug_division_by_zero_nodepth.xml",
359 "test_bt_bug_division_by_zero_nodepth.xml", "bug_division_by_zero_loop.db",
360 "bug_division_by_zero_loop", commandStrReverse)) {
361 ret = NO;
362 fprintf(stderr, "Test bug division by zero loop with no depth with backtracking: FAILED\n");
363 } else {
364 fprintf(stderr, "Test bug division by zero loop with no depth with backtracking: PASSED\n");
365 }
366
367 // Bug Division By Zero Loop depth 1
368 fprintf(stderr, "Testing bug division by zero loop depth 1...\n");
369 if (NO == runTest("ref_bug_division_by_zero_depth_1.xml",
370 "test_bug_division_by_zero_depth_1.xml", "bug_division_by_zero_loop.db",
371 "bug_division_by_zero_loop", commandStrDepthOne)) {
372 ret = NO;
373 fprintf(stderr, "Test bug division by zero loop with depth 1: FAILED\n");
374 } else {
375 fprintf(stderr, "Test bug division by zero loop with depth 1: PASSED\n");
376 }
377
378 // Bug Division By Zero Loop depth 1 with backtracking
379 fprintf(stderr, "Testing bug division by zero loop depth 1 with backtracking...\n");
380 if (NO == runTest("ref_bt_bug_division_by_zero_depth_1.xml",
381 "test_bt_bug_division_by_zero_depth_1.xml", "bug_division_by_zero_loop.db",
382 "bug_division_by_zero_loop", commandStrDepthOneReverse)) {
383 ret = NO;
384 fprintf(stderr, "Test bug division by zero loop with depth 1 with backtracking: FAILED\n");
385 } else {
386 fprintf(stderr, "Test bug division by zero loop with depth 1 with backtracking: PASSED\n");
387 }
388
389 // Bug Division By Zero Loop depth 1000
390 fprintf(stderr, "Testing bug division by zero loop depth 1000...\n");
391 if (NO == runTest("ref_bug_division_by_zero_depth_1.xml",
392 "test_bug_division_by_zero_depth_1000.xml", "bug_division_by_zero_loop.db",
393 "bug_division_by_zero_loop", commandStrDepthThousand)) {
394 ret = NO;
395 fprintf(stderr, "Test bug division by zero loop with depth 1000: FAILED\n");
396 } else {
397 fprintf(stderr, "Test bug division by zero loop with depth 1000: PASSED\n");
398 }
399
400 // Bug Bad Food
401 fprintf(stderr, "Testing bug bad food...\n");
402 if (NO == runTest("ref_bug_bad_food.xml",
403 "test_bug_bad_food.xml", "bug_bad_food.db",
404 "bug_bad_food", commandStr)) {
405 ret = NO;
406 fprintf(stderr, "Test bug bad food: FAILED\n");
407 } else {
408 fprintf(stderr, "Test bug bad food: PASSED\n");
409 }
410
411 return ret;
412}
diff --git a/pathologist/src/pathologist/xml_writer.c b/pathologist/src/pathologist/xml_writer.c
new file mode 100644
index 0000000..64775a4
--- /dev/null
+++ b/pathologist/src/pathologist/xml_writer.c
@@ -0,0 +1,126 @@
1/**
2 * @file monkey/xml_writer.c
3 * @brief Monkey API for generating XML debug report
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <assert.h>
9#include <string.h>
10#include "monkey_common.h"
11#include "pathologist_xml_writer.h"
12
13
14struct MONKEY_XML_Node* MONKEY_XML_WRITER_new_node(const char *name, const char *innerText) {
15 struct MONKEY_XML_Node *node = MONKEY_malloc(sizeof(struct MONKEY_XML_Node));
16 node->name = name;
17 node->innerText = innerText;
18 node->childrenListHead = NULL;
19 node->childrenListTail = NULL;
20 node->attributeListHead = NULL;
21 node->attributeListTail = NULL;
22 return node;
23}
24
25
26int MONKEY_XML_WRITER_add_attribute(struct MONKEY_XML_Node* node, const char *attrName, const char *attrValue)
27{
28 struct XmlAttribute *attribute = MONKEY_malloc(sizeof(struct XmlAttribute));
29 attribute->name = attrName;
30 attribute->value = attrValue;
31 attribute->next = NULL; attribute->prev = NULL;
32 MONKEY_CONTAINER_DLL_insert_tail (node->attributeListHead, node->attributeListTail,
33 attribute);
34 return MONKEY_OK;
35}
36
37
38int MONKEY_XML_WRITER_add_inner_text(struct MONKEY_XML_Node *node, const char *innerText)
39{
40 node->innerText = innerText;
41 return MONKEY_OK;
42}
43
44
45int MONKEY_XML_WRITER_delete_tree(struct MONKEY_XML_Node *root)
46{
47 struct MONKEY_XML_Node *tmp = NULL;
48 struct XmlAttribute *tmpAttribute = NULL;
49
50 /* MONKEY_freeing children list - Depth First */
51 while (NULL != root->childrenListHead) {
52 tmp = root->childrenListHead;
53 root->childrenListHead = tmp->next;
54 MONKEY_XML_WRITER_delete_tree(tmp);
55 }
56
57 /* MONKEY_freeing Attributes List */
58 while (NULL != root->attributeListHead) {
59 tmpAttribute = root->attributeListHead;
60 root->attributeListHead = tmpAttribute->next;
61 MONKEY_free(tmpAttribute);
62 }
63
64 /* MONKEY_freeing Leaf Node */
65 MONKEY_free(root);
66
67 return MONKEY_OK;
68}
69
70
71struct MONKEY_XML_Node* MONKEY_XML_WRITER_add_child(struct MONKEY_XML_Node *parent, struct MONKEY_XML_Node *child) {
72 child->next = NULL; child->prev = NULL;
73 MONKEY_CONTAINER_DLL_insert_tail (parent->childrenListHead, parent->childrenListTail,
74 child);
75 return child;
76}
77
78
79FILE* MONKEY_XML_WRITER_create_document(const char *filePath) {
80 FILE* file = fopen(filePath, "w");
81 if (NULL != file) {
82 fprintf(file, "<?xml version=\"1.0\"?>\n");
83 return file;
84 }
85 return NULL;
86}
87
88static char* escapeSpecialCharacters(const char * s) {
89 char* escaped;
90 escaped = MONKEY_str_replace_all (s, "&", "&amp;"); // & (escaping amp character MUST be the first, otherwise, it will spoil the other escaped characters
91 escaped = MONKEY_str_replace_all (escaped, "\"", "&quot;"); // single quote
92 escaped = MONKEY_str_replace_all (escaped, "'", "&apos;"); // double quotes
93 escaped = MONKEY_str_replace_all (escaped, "<", "&lt;"); // less than
94 escaped = MONKEY_str_replace_all (escaped, ">", "&gt;");// greater than
95
96 return escaped;
97}
98
99int MONKEY_XML_WRITER_write_document(FILE* file, struct MONKEY_XML_Node *root) {
100 struct MONKEY_XML_Node *tmp = root->childrenListHead;
101 struct XmlAttribute *tmpAttribute = root->attributeListHead;
102 if (!tmpAttribute)
103 fprintf(file, "<%s>", root->name);
104 else {
105 fprintf(file, "<%s ", root->name);
106 while (NULL != tmpAttribute) {
107 /* Escaping special characters in attribute values */
108 fprintf(file, "%s=\"%s\" ", tmpAttribute->name,
109 (tmpAttribute->value) ?
110 (escapeSpecialCharacters(tmpAttribute->value)) : "");
111 tmpAttribute = tmpAttribute->next;
112 }
113 fprintf(file, ">");
114 }
115 if(tmp) fprintf(file, "\n");
116
117 if (NULL != root->innerText)
118 fprintf(file, "%s", escapeSpecialCharacters(root->innerText));
119
120 while (NULL != tmp) {
121 MONKEY_XML_WRITER_write_document(file, tmp);
122 tmp = tmp->next;
123 }
124 fprintf(file, "</%s>\n", root->name);//End
125 return MONKEY_OK;
126}
diff --git a/pathologist/src/struct_parse/exmpl.c b/pathologist/src/struct_parse/exmpl.c
new file mode 100644
index 0000000..00d3a6f
--- /dev/null
+++ b/pathologist/src/struct_parse/exmpl.c
@@ -0,0 +1,31 @@
1#include <stdio.h>
2#include <stdlib.h>
3
4struct a {
5 char c;
6 char c2;
7 char* s[2];
8 struct a* a;
9 char r[4];
10 int i[2];
11 float f;
12};
13
14int main(int argc, char* argv[]) {
15 struct a a;
16 struct a b;
17 a.c = '\'';
18 a.c2 = '"';
19 a.s[0] = "bliblablu\"";
20 a.s[1] = "trololo";
21 a.a = &b;
22 a.r[0] = '\n';
23 a.r[1] = 'a';
24 a.r[2] = '';
25 a.r[3] = '"';
26 a.i[0] = -5;
27 a.i[1] = 42;
28 a.f = 3.141f;
29
30 return 0;
31}
diff --git a/pathologist/src/struct_parse/p.c b/pathologist/src/struct_parse/p.c
new file mode 100644
index 0000000..c99a654
--- /dev/null
+++ b/pathologist/src/struct_parse/p.c
@@ -0,0 +1,147 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5char* parse(char* str, int indent);
6
7// parses one pair of curly braces recursively.
8// expects first char to be the opening '{'
9// returns pointer to the next char after the closing '}' or NULL on error
10char* parse(char* str, int indent) {
11 int is_struct = 0, is_array = 0, had_equals = 0, had_children = 0, had_end = 0;
12 char* first = NULL;
13 char* second = NULL;
14 char* ws = NULL;
15 char* count = NULL;
16
17 if(*str++ != '{') return NULL;
18 while(*str == ' ') str++;
19 if(!*str) return NULL;
20 while(*str) {
21 first = str;
22 second = NULL;
23 had_equals = 0;
24 had_children = 0;
25 had_end = 0;
26 count = NULL;
27 while(*str) {
28 if(*str == '{') {
29 if( (is_struct && !had_equals) ||
30 (str != first && str != second) ) return NULL;
31 if(str == second) {
32 printf("%*s<expression name=\"%s\">\n", 2*indent, "", first);
33 had_children = 1;
34 }
35 str = parse(str, indent+1);
36 //continue; // have to check this char => no str++ at end of loop.
37 } else if(*str == '=') {
38 if(had_equals || is_array) return NULL;
39 had_equals = 1;
40 if(!is_struct && !is_array)
41 is_struct = 1;
42 for(ws = str-1; *ws == ' '; ws--)
43 *ws = '\0';
44 *str = '\0';
45 while(*++str == ' ');
46 second = str;
47 continue; // have to check this char => no str++ at end of loop.
48 } else if(*str == '\'' || *str == '"') {
49 char end_char = *str;
50 if(had_equals) second = str;
51 else first = str;
52 for(str++; *str != end_char; str++) {
53 if(!*str) return NULL;
54 if(*str == '\\') {
55 if(!*(str+1)) return NULL;
56 else str++;
57 }
58 }
59 } else if(*str == '<') {
60 for(ws = str-1; *ws == ' '; ws--)
61 *ws = '\0';
62 if(!(str = strpbrk(str+1, "0123456789>"))) return NULL;
63 if(*str == '>') continue;
64 count = str;
65 if(!(str = strpbrk(str+1, " >"))) return NULL;
66 if(*str == ' ')
67 *str = '\0';
68 else {
69 *str = '\0';
70 if(!(str = strchr(str+1, '>'))) return NULL;
71 }
72 } else if(*str == ',') {
73 if(is_struct && !had_equals) return NULL;
74 is_array = !is_struct;
75 is_struct = !is_array;
76 break;
77 } else if(*str == '}') {
78 had_end = 1;
79 break;
80 }
81 str++;
82 }
83 for(ws = str-1; *ws == ' '; ws--)
84 *ws = '\0';
85 *str = '\0';
86 if(had_children)
87 printf("%*s</expression>\n", 2*indent, "");
88 else if(is_array)
89 if(count)
90 printf("%*s<element count=\"%s\">%s</element>\n", 2*indent, "", count, first);
91 else
92 printf("%*s<element>%s</element>\n", 2*indent, "", first);
93 else
94 printf("%*s<expression name=\"%s\">%s</expression>\n", 2*indent, "", first, second);
95 if(had_end) return str;
96 str++;
97 while(*str == ' ') str++;
98 }
99 return NULL;
100}
101
102
103char * getl(void) {
104 char * line = malloc(128), * linep = line;
105 size_t lenmax = 128, len = lenmax;
106 int c;
107
108 if(line == NULL)
109 return NULL;
110
111 for(;;) {
112 c = fgetc(stdin);
113 if(c == EOF)
114 break;
115
116 if(--len == 0) {
117 len = lenmax;
118 char * linen = realloc(linep, lenmax *= 2);
119
120 if(linen == NULL) {
121 free(linep);
122 return NULL;
123 }
124 line = linen + (line - linep);
125 linep = linen;
126 }
127
128 if((*line = c) == '\n')
129 break;
130 line++;
131 }
132 *line = '\0';
133 return linep;
134}
135
136
137int main(int argc, char* argv[]) {
138 char* line;
139 while(line = getl()) {
140 if(*line == '\0') break;
141 if(!parse(line, 1))
142 printf("=====FAILED!=====\n\n");
143 free(line);
144 }
145 if(line) free(line);
146 return 0;
147}
diff --git a/pathologist/src/struct_parse/test.sh b/pathologist/src/struct_parse/test.sh
new file mode 100644
index 0000000..2ab8cec
--- /dev/null
+++ b/pathologist/src/struct_parse/test.sh
@@ -0,0 +1,2 @@
1#!/bin/sh
2for n in `seq 1 1000`; do zzuf -r 0.00$n -s $n -i valgrind -q --leak-check=summary --track-origins=yes ./a.out < test.txt; done
diff --git a/pathologist/src/struct_parse/test.txt b/pathologist/src/struct_parse/test.txt
new file mode 100644
index 0000000..030fe36
--- /dev/null
+++ b/pathologist/src/struct_parse/test.txt
@@ -0,0 +1 @@
{value = {publish = {pc = 0x607a20, fi = 0x60b580, cctx = 0x0, pctx = 0x0, filename = 0x60b9e0 "/home/grothoff/svn/gnunet/src/core/../../COPYING", size = 35147, eta = { rel_value = 18446744073709551615}, duration = {rel_value = 1360752708604}, completed = 0, anonymity = 1, specifics = {progress = {data = 0x0, offset = 0, data_len = 140737349529600, depth = 4158564850}, resume = {message = 0x0, chk_uri = 0x0}, completed = {chk_uri = 0x0}, error = {message = 0x0}}}, download = {dc = 0x607a20, cctx = 0x60b580, pctx = 0x0, sctx = 0x0, uri = 0x60b9e0, filename = 0x894b <Address 0x894b out of bounds>, size = 18446744073709551615, eta = {rel_value = 1360752708604}, duration = { rel_value = 0}, completed = 1, anonymity = 0, is_active = 0, specifics = {progress = {data = 0x0, offset = 140737349529600, data_len = 140737351952882, block_download_duration = { rel_value = 140733193388033}, depth = 0, respect_offered = 0, num_transmissions = 6336704}, start = {meta = 0x0}, resume = {meta = 0x0, message = 0x7ffff7b9b000 "\177ELF\002\001\001"}, error = {message = 0x0}}}, search = {sc = 0x607a20, cctx = 0x60b580, pctx = 0x0, query = 0x0, duration = {rel_value = 6339040}, anonymity = 35147, specifics = {result = {meta = 0xffffffffffffffff, uri = 0x13cd32e8bfc, result = 0x0, applicability_rank = 1}, resume_result = {meta = 0xffffffffffffffff, uri = 0x13cd32e8bfc, result = 0x0, availability_rank = 1, availability_certainty = 0, applicability_rank = 0}, update = {cctx = 0xffffffffffffffff, meta = 0x13cd32e8bfc, uri = 0x0, availability_rank = 1, availability_certainty = 0, applicability_rank = 0, current_probe_time = {rel_value = 0}}, result_suspend = {cctx = 0xffffffffffffffff, meta = 0x13cd32e8bfc, uri = 0x0}, result_stopped = {cctx = 0xffffffffffffffff, meta = 0x13cd32e8bfc, uri = 0x0}, resume = { message = 0xffffffffffffffff <Address 0xffffffffffffffff out of bounds>, is_paused = -751924228}, error = { message = 0xffffffffffffffff <Address 0xffffffffffffffff out of bounds>}, ns = {ns = 0xffffffffffffffff, name = 0x13cd32e8bfc <Address 0x13cd32e8bfc out of bounds>, root = 0x0, meta = 0x1, id = {bits = {0, 0, 0, 0, 4156141568, 32767, 4158564850, 32767, 1, 32767, 0, 0, 6336704, 0, 4156149880, 32767}}}}}, unindex = {uc = 0x607a20, cctx = 0x60b580, filename = 0x0, size = 0, eta = {rel_value = 6339040}, duration = {rel_value = 35147}, completed = 18446744073709551615, specifics = {progress = {data = 0x13cd32e8bfc, offset = 0, data_len = 1, depth = 0}, resume = {message = 0x13cd32e8bfc <Address 0x13cd32e8bfc out of bounds>}, error = {message = 0x13cd32e8bfc <Address 0x13cd32e8bfc out of bounds>}}}}, status = GNUNET_FS_STATUS_PUBLISH_START}
diff --git a/pathologist/src/struct_parse/test1.txt b/pathologist/src/struct_parse/test1.txt
new file mode 100644
index 0000000..9ddec90
--- /dev/null
+++ b/pathologist/src/struct_parse/test1.txt
@@ -0,0 +1 @@
{0 <repeats 9274 times>, -136430974, 32767, 0 <repeats 18 times>, 1560576, 0, 1560100, 0, 1560100, 0, 0, 0, 5, 0, 3657728, 0, 3678208, 0, 3677624, 0, 3696728, 0, 1560576, 0, 3, 0 <repeats 47 times>, -136436024, 32767, 0, 0, 0, 0, 0, 0, -134241024, 32767, 47, 0, -136413867, 32767, 0, 0, -134239856, 32767, 32, 0, 0, 1, -134241104, 32767, 0, 0, -8304, 32767, -136414759, 32767, -134225464, 32767, -134240992, 32767, -8304, 32767, -136431348, 32767, 1811964521, 778265193, 909012851, 1932419840, 3550831, 3550831, -136398771, 32767, 0, 0, -140067357, 32767, 0, 0, -136427454, 32767, -136478720, 32767, -136460200, 32767, -136479304, 32767, 0, 0, -8656, 32767, -134241024, 32767, 4195056, 0, -134225504, 32767, 3, 1, -8136, 0, 6, 0, 3696728, 0, 2053, 0, 1324524, 0, 1, 0, 33261, 0, 0, 0, 0, 0, 1583120, 0, 4096, 0, 3104, 0, 1349347463, 0, 0, 0, 1342975530, 0, 0, 0, 1349347464, 0 <repeats 11 times>, 4195056, 0, -134225464, 32767, -134229952, 32767, 0, 0, -134224528, 32767, 0, 0, -136427174, 32767, 0, 32767, -7304, 32767, 0, 0, 0, 0, 0, 0, -134261768, 32767, 0, 0, 7, 0, 0, 1...}
diff --git a/pathologist/src/struct_parse/test2.txt b/pathologist/src/struct_parse/test2.txt
new file mode 100644
index 0000000..e89ac3a
--- /dev/null
+++ b/pathologist/src/struct_parse/test2.txt
@@ -0,0 +1 @@
{"buil\"e" , '" ueuil \"\'', "\\\\", 0 ,1560576,0, 1560100,0 , 1560100}
diff --git a/pathologist/src/struct_parse/test3.txt b/pathologist/src/struct_parse/test3.txt
new file mode 100644
index 0000000..39bbf8c
--- /dev/null
+++ b/pathologist/src/struct_parse/test3.txt
@@ -0,0 +1 @@
{data = 0x0,offset=0, data_len= 140737349529600 ,depth =4158564850 }
diff --git a/pathologist/src/struct_parse/test4.txt b/pathologist/src/struct_parse/test4.txt
new file mode 100644
index 0000000..3735a52
--- /dev/null
+++ b/pathologist/src/struct_parse/test4.txt
@@ -0,0 +1 @@
{0 <repeats 9274 times>, -136430974, 32767, 0 <repeats 18 times>, 1560576, 0,1560100, 0, 1560100, 0, 0}
diff --git a/pathologist/src/util/Makefile.am b/pathologist/src/util/Makefile.am
new file mode 100644
index 0000000..6961228
--- /dev/null
+++ b/pathologist/src/util/Makefile.am
@@ -0,0 +1,12 @@
1INCLUDES = -I$(top_srcdir)/src/include -I$(top_builddir)/src/include
2
3if USE_COVERAGE
4 AM_CFLAGS = --coverage -O0
5 XLIB = -lgcov
6endif
7
8lib_LTLIBRARIES = libpathologistutil.la
9
10libpathologistutil_la_SOURCES = \
11 common_allocation.c \
12 str_replace_all.c \ No newline at end of file
diff --git a/pathologist/src/util/common_allocation.c b/pathologist/src/util/common_allocation.c
new file mode 100644
index 0000000..2eef7d5
--- /dev/null
+++ b/pathologist/src/util/common_allocation.c
@@ -0,0 +1,338 @@
1/*
2 This file is part of MONKEY.
3 (C) 2001, 2002, 2003, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5 MONKEY is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 MONKEY is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with MONKEY; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file util/common_allocation.c
23 * @brief wrapper around malloc/free
24 * @author Christian Grothoff
25 * @author Safey A.Halim
26 */
27
28#include "monkey_common.h"
29#include <stdlib.h>
30#include <string.h>
31#include <stdarg.h>
32
33#ifndef INT_MAX
34#define INT_MAX 0x7FFFFFFF
35#endif
36
37#if 0
38#define W32_MEM_LIMIT 200000000
39#endif
40
41#ifdef W32_MEM_LIMIT
42static LONG mem_used = 0;
43#endif
44
45/**
46 * Allocate memory. Checks the return value, aborts if no more
47 * memory is available.
48 *
49 * @param size how many bytes of memory to allocate, do NOT use
50 * this function (or MONKEY_malloc) to allocate more than several MB
51 * of memory, if you are possibly needing a very large chunk use
52 * MONKEY_xmalloc_unchecked_ instead.
53 * @param filename where in the code was the call to MONKEY_malloc
54 * @param linenumber where in the code was the call to MONKEY_malloc
55 * @return pointer to size bytes of memory
56 */
57void *
58MONKEY_xmalloc_ (size_t size, const char *filename, int linenumber)
59{
60 void *ret;
61
62 /* As a security precaution, we generally do not allow very large
63 * allocations using the default 'MONKEY_malloc' macro */
64 MONKEY_assert_at (size <= MONKEY_MAX_MALLOC_CHECKED, filename, linenumber);
65 ret = MONKEY_xmalloc_unchecked_ (size, filename, linenumber);
66 if (ret == NULL)
67 {
68 fprintf (stderr, "malloc");
69 abort ();
70 }
71 return ret;
72}
73
74
75/**
76 * Allocate and initialize memory. Checks the return value, aborts if no more
77 * memory is available. Don't use MONKEY_xmemdup_ directly. Use the
78 * MONKEY_memdup macro.
79 *
80 * @param buf buffer to initialize from (must contain size bytes)
81 * @param size number of bytes to allocate
82 * @param filename where is this call being made (for debugging)
83 * @param linenumber line where this call is being made (for debugging)
84 * @return allocated memory, never NULL
85 */
86void *
87MONKEY_xmemdup_ (const void *buf, size_t size, const char *filename,
88 int linenumber)
89{
90 void *ret;
91
92 /* As a security precaution, we generally do not allow very large
93 * allocations here */
94 MONKEY_assert_at (size <= MONKEY_MAX_MALLOC_CHECKED, filename, linenumber);
95#ifdef W32_MEM_LIMIT
96 size += sizeof (size_t);
97 if (mem_used + size > W32_MEM_LIMIT)
98 return NULL;
99#endif
100 MONKEY_assert_at (size < INT_MAX, filename, linenumber);
101 ret = malloc (size);
102 if (ret == NULL)
103 {
104 fprintf (stderr, "malloc");
105 abort ();
106 }
107#ifdef W32_MEM_LIMIT
108 *((size_t *) ret) = size;
109 ret = &((size_t *) ret)[1];
110 mem_used += size;
111#endif
112 memcpy (ret, buf, size);
113 return ret;
114}
115
116
117
118/**
119 * Wrapper around malloc. Allocates size bytes of memory.
120 * The memory will be zero'ed out.
121 *
122 * @param size the number of bytes to allocate
123 * @param filename where in the code was the call to MONKEY_malloc_large
124 * @param linenumber where in the code was the call to MONKEY_malloc_large
125 * @return pointer to size bytes of memory, NULL if we do not have enough memory
126 */
127void *
128MONKEY_xmalloc_unchecked_ (size_t size, const char *filename, int linenumber)
129{
130 void *result;
131
132#ifdef W32_MEM_LIMIT
133 size += sizeof (size_t);
134 if (mem_used + size > W32_MEM_LIMIT)
135 return NULL;
136#endif
137
138 MONKEY_assert_at (size < INT_MAX, filename, linenumber);
139 result = malloc (size);
140 if (result == NULL)
141 return NULL;
142 memset (result, 0, size);
143
144#ifdef W32_MEM_LIMIT
145 *((size_t *) result) = size;
146 result = &((size_t *) result)[1];
147 mem_used += size;
148#endif
149
150 return result;
151}
152
153
154/**
155 * Reallocate memory. Checks the return value, aborts if no more
156 * memory is available.
157 *
158 * @param ptr the pointer to reallocate
159 * @param n how many bytes of memory to allocate
160 * @param filename where in the code was the call to MONKEY_realloc
161 * @param linenumber where in the code was the call to MONKEY_realloc
162 * @return pointer to size bytes of memory
163 */
164void *
165MONKEY_xrealloc_ (void *ptr, size_t n, const char *filename, int linenumber)
166{
167#ifdef W32_MEM_LIMIT
168 n += sizeof (size_t);
169 ptr = &((size_t *) ptr)[-1];
170 mem_used = mem_used - *((size_t *) ptr) + n;
171#endif
172 ptr = realloc (ptr, n);
173 if ((NULL == ptr) && (n > 0))
174 {
175 fprintf (stderr, "realloc");
176 abort ();
177 }
178#ifdef W32_MEM_LIMIT
179 ptr = &((size_t *) ptr)[1];
180#endif
181 return ptr;
182}
183
184
185/**
186 * Free memory. Merely a wrapper for the case that we
187 * want to keep track of allocations.
188 *
189 * @param ptr the pointer to free
190 * @param filename where in the code was the call to MONKEY_array_grow
191 * @param linenumber where in the code was the call to MONKEY_array_grow
192 */
193void
194MONKEY_xfree_ (void *ptr, const char *filename, int linenumber)
195{
196 MONKEY_assert_at (ptr != NULL, filename, linenumber);
197#ifdef W32_MEM_LIMIT
198 ptr = &((size_t *) ptr)[-1];
199 mem_used -= *((size_t *) ptr);
200#endif
201 free (ptr);
202}
203
204/**
205 * Dup a string (same semantics as strdup).
206 *
207 * @param str the string to dup
208 * @param filename where in the code was the call to MONKEY_strdup
209 * @param linenumber where in the code was the call to MONKEY_strdup
210 * @return strdup(str)
211 */
212char *
213MONKEY_xstrdup_ (const char *str, const char *filename, int linenumber)
214{
215 char *res;
216
217 MONKEY_assert_at (str != NULL, filename, linenumber);
218 res = MONKEY_xmalloc_ (strlen (str) + 1, filename, linenumber);
219 memcpy (res, str, strlen (str) + 1);
220 return res;
221}
222
223
224/**
225 * Dup partially a string (same semantics as strndup).
226 *
227 * @param str the string to dup
228 * @param len the length of the string to dup
229 * @param filename where in the code was the call to MONKEY_strndup
230 * @param linenumber where in the code was the call to MONKEY_strndup
231 * @return strndup(str,len)
232 */
233char *
234MONKEY_xstrndup_ (const char *str, size_t len, const char *filename,
235 int linenumber)
236{
237 char *res;
238
239 MONKEY_assert_at (str != NULL, filename, linenumber);
240 len = MONKEY_MIN (len, strlen (str));
241 res = MONKEY_xmalloc_ (len + 1, filename, linenumber);
242 memcpy (res, str, len);
243 res[len] = '\0';
244 return res;
245}
246
247
248/**
249 * Grow an array. Grows old by (*oldCount-newCount)*elementSize bytes
250 * and sets *oldCount to newCount.
251 *
252 * @param old address of the pointer to the array
253 * *old may be NULL
254 * @param elementSize the size of the elements of the array
255 * @param oldCount address of the number of elements in the *old array
256 * @param newCount number of elements in the new array, may be 0
257 * @param filename where in the code was the call to MONKEY_array_grow
258 * @param linenumber where in the code was the call to MONKEY_array_grow
259 */
260void
261MONKEY_xgrow_ (void **old, size_t elementSize, unsigned int *oldCount,
262 unsigned int newCount, const char *filename, int linenumber)
263{
264 void *tmp;
265 size_t size;
266
267 MONKEY_assert_at (INT_MAX / elementSize > newCount, filename, linenumber);
268 size = newCount * elementSize;
269 if (size == 0)
270 {
271 tmp = NULL;
272 }
273 else
274 {
275 tmp = MONKEY_xmalloc_ (size, filename, linenumber);
276 memset (tmp, 0, size); /* client code should not rely on this, though... */
277 if (*oldCount > newCount)
278 *oldCount = newCount; /* shrink is also allowed! */
279 memcpy (tmp, *old, elementSize * (*oldCount));
280 }
281
282 if (*old != NULL)
283 {
284 MONKEY_xfree_ (*old, filename, linenumber);
285 }
286 *old = tmp;
287 *oldCount = newCount;
288}
289
290
291/**
292 * Like asprintf, just portable.
293 *
294 * @param buf set to a buffer of sufficient size (allocated, caller must free)
295 * @param format format string (see printf, fprintf, etc.)
296 * @param ... data for format string
297 * @return number of bytes in "*buf" excluding 0-termination
298 */
299int
300MONKEY_asprintf (char **buf, const char *format, ...)
301{
302 int ret;
303 va_list args;
304
305 va_start (args, format);
306 ret = vsnprintf (NULL, 0, format, args);
307 va_end (args);
308 *buf = MONKEY_malloc (ret + 1);
309 va_start (args, format);
310 ret = vsprintf (*buf, format, args);
311 va_end (args);
312 return ret;
313}
314
315
316/**
317 * Like snprintf, just aborts if the buffer is of insufficient size.
318 *
319 * @param buf pointer to buffer that is written to
320 * @param size number of bytes in buf
321 * @param format format strings
322 * @param ... data for format string
323 * @return number of bytes written to buf or negative value on error
324 */
325int
326MONKEY_snprintf (char *buf, size_t size, const char *format, ...)
327{
328 int ret;
329 va_list args;
330
331 va_start (args, format);
332 ret = vsnprintf (buf, size, format, args);
333 va_end (args);
334 MONKEY_assert (ret <= size);
335 return ret;
336}
337
338/* end of common_allocation.c */
diff --git a/pathologist/src/util/str_replace_all.c b/pathologist/src/util/str_replace_all.c
new file mode 100644
index 0000000..a167523
--- /dev/null
+++ b/pathologist/src/util/str_replace_all.c
@@ -0,0 +1,56 @@
1/**
2 * vim: tabstop=2:shiftwidth=2:softtabstop=2:expandtab
3 *
4 * str_replace.c implements a str_replace PHP like function
5 * Copyright (C) 2010 chantra <chantra__A__debuntu__D__org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 * gcc -o str_replace_all str_replace_all.c
22 */
23
24#include <string.h>
25#include "monkey_common.h"
26
27char *
28MONKEY_str_replace_all ( const char *string, const char *substr, const char *replacement ){
29 char *tok = NULL;
30 char *newstr = NULL;
31 char *oldstr = NULL;
32 char *head = NULL;
33
34 /* if either substr or replacement is NULL, duplicate string a let caller handle it */
35 if ( substr == NULL || replacement == NULL ) return MONKEY_strdup (string);
36 newstr = MONKEY_strdup (string);
37 head = newstr;
38 while ( (tok = strstr ( head, substr ))){
39 oldstr = newstr;
40 newstr = MONKEY_malloc ( strlen ( oldstr ) - strlen ( substr ) + strlen ( replacement ) + 1 );
41 /*failed to alloc mem, free old string and return NULL */
42 if ( newstr == NULL ){
43 MONKEY_free (oldstr);
44 return NULL;
45 }
46 memcpy ( newstr, oldstr, tok - oldstr );
47 memcpy ( newstr + (tok - oldstr), replacement, strlen ( replacement ) );
48 memcpy ( newstr + (tok - oldstr) + strlen( replacement ), tok + strlen ( substr ), strlen ( oldstr ) - strlen ( substr ) - ( tok - oldstr ) );
49 memset ( newstr + strlen ( oldstr ) - strlen ( substr ) + strlen ( replacement ) , 0, 1 );
50 /* move back head right after the last replacement */
51 head = newstr + (tok - oldstr) + strlen( replacement );
52 MONKEY_free (oldstr);
53 }
54 return newstr;
55}
56