Better error messages.
[wine] / debugger / source.c
1 /*
2  * File source.c - source file handling for internal debugger.
3  *
4  * Copyright (C) 1997, Eric Youngdale.
5  *
6  */
7
8 #include "config.h"
9 #include <stdio.h>
10 #include <stdlib.h>
11
12 #include <sys/types.h>
13 #ifdef HAVE_SYS_MMAN_H
14 #include <sys/mman.h>
15 #endif
16 #include <fcntl.h>
17 #include <sys/stat.h>
18 #include <limits.h>
19 #include <string.h>
20 #include <unistd.h>
21 #ifndef PATH_MAX
22 #define PATH_MAX _MAX_PATH
23 #endif
24
25 #include "wine/winbase16.h"
26 #include "pe_image.h"
27 #include "peexe.h"
28 #include "debugger.h"
29 #include "task.h"
30
31 struct searchlist
32 {
33   char * path;
34   struct searchlist * next;
35 };
36
37
38 struct open_filelist
39 {
40   char                  * path;
41   char                  * real_path;
42   struct open_filelist  * next;
43   unsigned int            size;
44   signed int              nlines;
45   unsigned int          * linelist;
46 };
47
48 static struct open_filelist * ofiles;
49
50 static struct searchlist * listhead;
51 static char DEBUG_current_sourcefile[PATH_MAX];
52 static int DEBUG_start_sourceline = -1;
53 static int DEBUG_end_sourceline = -1;
54
55 void
56 DEBUG_ShowDir()
57 {
58   struct searchlist * sl;
59
60   fprintf(stderr,"Search list :\n");
61   for(sl = listhead; sl; sl = sl->next)
62     {
63       fprintf(stderr, "\t%s\n", sl->path);
64     }
65   fprintf(stderr, "\n");
66 }
67
68 void
69 DEBUG_AddPath(const char * path)
70 {
71   struct searchlist * sl;
72
73   sl = (struct searchlist *) DBG_alloc(sizeof(struct searchlist));
74   if( sl == NULL )
75     {
76       return;
77     }
78
79   sl->next = listhead;
80   sl->path = DBG_strdup(path);
81   listhead = sl;
82 }
83
84 void
85 DEBUG_NukePath()
86 {
87   struct searchlist * sl;
88   struct searchlist * nxt;
89
90   for(sl = listhead; sl; sl = nxt)
91     {
92       nxt = sl->next;
93       DBG_free(sl->path);
94       DBG_free(sl);
95     }
96
97   listhead = NULL;
98 }
99
100 static
101 int
102 DEBUG_DisplaySource(char * sourcefile, int start, int end)
103 {
104   char                        * addr;
105   char                          buffer[1024];
106   int                           fd;
107   int                           i;
108   struct open_filelist        * ol;
109   int                           nlines;
110   char                        * basename;
111   char                        * pnt;
112   int                           rtn;
113   struct searchlist           * sl;
114   struct stat                   statbuf;
115   int                           status;
116   char                          tmppath[PATH_MAX];
117
118   /*
119    * First see whether we have the file open already.  If so, then
120    * use that, otherwise we have to try and open it.
121    */
122   for(ol = ofiles; ol; ol = ol->next)
123     {
124       if( strcmp(ol->path, sourcefile) == 0 )
125         {
126           break;
127         }
128     }
129
130   if( ol == NULL )
131     {
132       /*
133        * Try again, stripping the path from the opened file.
134        */
135       basename = strrchr(sourcefile, '\\' );
136       if ( !basename )
137           basename = strrchr(sourcefile, '/' );
138       if ( !basename )
139           basename = sourcefile;
140       else
141           basename++;
142
143       for(ol = ofiles; ol; ol = ol->next)
144         {
145           if( strcmp(ol->path, basename) == 0 )
146             {
147               break;
148             }
149         }
150       
151     }
152
153   if( ol == NULL )
154     {
155       /*
156        * Crapola.  We need to try and open the file.
157        */
158       status = stat(sourcefile, &statbuf);
159       if( status != -1 )
160         {
161           strcpy(tmppath, sourcefile);
162         }
163       else if( (status = stat(basename, &statbuf)) != -1 )
164         {
165           strcpy(tmppath, basename);
166         }
167       else
168         {
169           for(sl = listhead; sl; sl = sl->next)
170             {
171               strcpy(tmppath, sl->path);
172               if( tmppath[strlen(tmppath)-1] != '/' )
173                 {
174                   strcat(tmppath, "/");
175                 }
176               /*
177                * Now append the base file name.
178                */
179               strcat(tmppath, basename);
180               
181               status = stat(tmppath, &statbuf);
182               if( status != -1 )
183                 {
184                   break;
185                 }
186             }
187           
188           if( sl == NULL )
189             {
190               /*
191                * Still couldn't find it.  Ask user for path to add.
192                */
193               fprintf(stderr,"Enter path to file %s: ", sourcefile);
194               fgets(tmppath, sizeof(tmppath), stdin);
195               
196               if( tmppath[strlen(tmppath)-1] == '\n' )
197                 {
198                   tmppath[strlen(tmppath)-1] = '\0';
199                 }
200               
201               if( tmppath[strlen(tmppath)-1] != '/' )
202                 {
203                   strcat(tmppath, "/");
204                 }
205               /*
206                * Now append the base file name.
207                */
208               strcat(tmppath, basename);
209               
210               status = stat(tmppath, &statbuf);
211               if( status == -1 )
212                 {
213                   /*
214                    * OK, I guess the user doesn't really want to see it
215                    * after all.
216                    */
217                   ol = (struct open_filelist *) DBG_alloc(sizeof(*ol));
218                   ol->path = DBG_strdup(sourcefile);
219                   ol->real_path = NULL;
220                   ol->next = ofiles;
221                   ol->nlines = 0;
222                   ol->linelist = NULL;
223                   ofiles = ol;
224                   fprintf(stderr,"Unable to open file %s\n", tmppath);
225                   return FALSE;
226                 }
227             }
228         }
229       /*
230        * Create header for file.
231        */
232       ol = (struct open_filelist *) DBG_alloc(sizeof(*ol));
233       ol->path = DBG_strdup(sourcefile);
234       ol->real_path = DBG_strdup(tmppath);
235       ol->next = ofiles;
236       ol->nlines = 0;
237       ol->linelist = NULL;
238       ol->size = statbuf.st_size;
239       ofiles = ol;
240
241       /*
242        * Now open and map the file.
243        */
244       fd = open(tmppath, O_RDONLY);
245       if( fd == -1 )
246         {
247           return FALSE;
248         }
249
250       addr = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
251       if( addr == (char *) -1 )
252         {
253           return FALSE;
254         }
255
256       /*
257        * Now build up the line number mapping table.
258        */
259       ol->nlines = 1;
260       pnt = addr;
261       while(pnt < addr + ol->size )
262         {
263           if( *pnt++ == '\n' )
264             {
265               ol->nlines++;
266             }
267         }
268
269       ol->nlines++;
270       ol->linelist = (unsigned int*) DBG_alloc( ol->nlines * sizeof(unsigned int) );
271
272       nlines = 0;
273       pnt = addr;
274       ol->linelist[nlines++] = 0;
275       while(pnt < addr + ol->size )
276         {
277           if( *pnt++ == '\n' )
278             {
279               ol->linelist[nlines++] = pnt - addr;
280             }
281         }
282       ol->linelist[nlines++] = pnt - addr;
283
284     }
285   else
286     {
287       /*
288        * We know what the file is, we just need to reopen it and remap it.
289        */
290       fd = open(ol->real_path, O_RDONLY);
291       if( fd == -1 )
292         {
293           return FALSE;
294         }
295       
296       addr = mmap(0, ol->size, PROT_READ, MAP_PRIVATE, fd, 0);
297       if( addr == (char *) -1 )
298         {
299           return FALSE;
300         }
301     }
302   
303   /*
304    * All we need to do is to display the source lines here.
305    */
306   rtn = FALSE;
307   for(i=start - 1; i <= end - 1; i++)
308     {
309       if( i < 0 || i >= ol->nlines - 1)
310         {
311           continue;
312         }
313
314       rtn = TRUE;
315       memset(&buffer, 0, sizeof(buffer));
316       if( ol->linelist[i+1] != ol->linelist[i] )
317         {
318           memcpy(&buffer, addr + ol->linelist[i], 
319                  (ol->linelist[i+1] - ol->linelist[i]) - 1);
320         }
321       fprintf(stderr,"%d\t%s\n", i + 1,  buffer);
322     }
323
324   munmap(addr, ol->size);
325   close(fd);
326
327   return rtn;
328
329 }
330
331 void
332 DEBUG_List(struct list_id * source1, struct list_id * source2,
333                          int delta)
334 {
335   int    end;
336   int    rtn;
337   int    start;
338   char * sourcefile;
339
340   /*
341    * We need to see what source file we need.  Hopefully we only have
342    * one specified, otherwise we might as well punt.
343    */
344   if( source1 != NULL 
345       && source2 != NULL 
346       && source1->sourcefile != NULL
347       && source2->sourcefile != NULL 
348       && strcmp(source1->sourcefile, source2->sourcefile) != 0 )
349     {
350       fprintf(stderr, "Ambiguous source file specification.\n");
351       return;
352     }
353
354   sourcefile = NULL;
355   if( source1 != NULL && source1->sourcefile != NULL )
356     {
357       sourcefile = source1->sourcefile;
358     }
359
360   if( sourcefile == NULL 
361       && source2 != NULL 
362       && source2->sourcefile != NULL )
363     {
364       sourcefile = source2->sourcefile;
365     }
366
367   if( sourcefile == NULL )
368     {
369       sourcefile = (char *) &DEBUG_current_sourcefile;
370     }
371
372   if( sourcefile == NULL )
373     {
374       fprintf(stderr, "No source file specified.\n");
375       return;
376     }
377
378   /*
379    * Now figure out the line number range to be listed.
380    */
381   start = -1;
382   end = -1;
383
384   if( source1 != NULL )
385     {
386       start = source1->line;
387     }
388
389   if( source2 != NULL )
390     {
391       end = source2->line;
392     }
393
394   if( start == -1 && end == -1 )
395     {
396       if( delta < 0 )
397         {
398           end = DEBUG_start_sourceline;
399           start = end + delta;
400         }
401       else
402         {
403           start = DEBUG_end_sourceline;
404           end = start + delta;
405         }
406     }
407   else if( start == -1 )
408     {
409       start = end + delta;
410     }
411   else if (end == -1)
412     {
413       end = start + delta;
414     }
415
416   /*
417    * Now call this function to do the dirty work.
418    */
419   rtn = DEBUG_DisplaySource(sourcefile, start, end);
420
421   if( sourcefile != (char *) &DEBUG_current_sourcefile )
422     {
423       strcpy(DEBUG_current_sourcefile, sourcefile);
424     }
425   DEBUG_start_sourceline = start;
426   DEBUG_end_sourceline = end;
427 }
428
429 DBG_ADDR DEBUG_LastDisassemble={NULL,0,0};
430
431 void DEBUG_GetCurrentAddress( DBG_ADDR *addr )
432 {
433 #ifdef __i386__
434     TDB *pTask = (TDB*)GlobalLock16( GetCurrentTask() );
435
436     addr->type = NULL;
437     addr->seg  = CS_reg(&DEBUG_context);
438     addr->off  = EIP_reg(&DEBUG_context);
439
440     if (ISV86(&DEBUG_context)) addr->seg |= (DWORD)(pTask? pTask->hModule : 0) << 16; 
441     else if (IS_SELECTOR_SYSTEM(addr->seg)) addr->seg = 0;
442
443     GlobalUnlock16( GetCurrentTask() );
444 #else
445     addr->type = NULL;
446     addr->seg  = 0;
447     addr->off  = (DWORD)GET_IP(&DEBUG_context);
448 #endif
449 }
450
451
452 static int
453 _disassemble(DBG_ADDR *addr)
454 {
455         DEBUG_PrintAddress( addr, dbg_mode, TRUE );
456         fprintf(stderr,": ");
457         if (!DBG_CHECK_READ_PTR( addr, 1 )) return 0;
458         DEBUG_Disasm( addr, TRUE );
459         fprintf(stderr,"\n");
460         return 1;
461 }
462
463 void
464 _disassemble_fixaddr(DBG_ADDR *addr) {
465     DWORD seg2;
466     struct datatype *testtype;
467
468     DBG_FIX_ADDR_SEG(addr,CS_reg(&DEBUG_context));
469     if( addr->type != NULL )
470       {
471         if( addr->type == DEBUG_TypeIntConst )
472           {
473             /*
474              * We know that we have the actual offset stored somewhere
475              * else in 32-bit space.  Grab it, and we
476              * should be all set.
477              */
478             seg2 = addr->seg;
479             addr->seg = 0;
480             addr->off = DEBUG_GetExprValue(addr, NULL);
481             addr->seg = seg2;
482           }
483         else
484           {
485             if (!DBG_CHECK_READ_PTR( addr, 1 )) return;
486             DEBUG_TypeDerefPointer(addr, &testtype);
487             if( testtype != NULL || addr->type == DEBUG_TypeIntConst )
488                 addr->off = DEBUG_GetExprValue(addr, NULL);
489           }
490       }
491     else if (!addr->seg && !addr->off)
492     {
493         fprintf(stderr,"Invalid expression\n");
494         return;
495     }
496 }
497
498 void
499 DEBUG_Disassemble(const DBG_ADDR *xstart,const DBG_ADDR *xend,int offset)
500 {
501   int i;
502   DBG_ADDR      last;
503   DBG_ADDR      end,start;
504
505   if (xstart) {
506     start=*xstart;
507     _disassemble_fixaddr(&start);
508   }
509   if (xend) {
510     end=*xend;
511     _disassemble_fixaddr(&end);
512   }
513   if (!xstart && !xend) {
514     last = DEBUG_LastDisassemble;
515     if (!last.seg && !last.off)
516       DEBUG_GetCurrentAddress( &last );
517
518     for (i=0;i<offset;i++)
519       if (!_disassemble(&last)) break;
520     memcpy(&DEBUG_LastDisassemble,&last,sizeof(last));
521     return;
522   }
523   last = start;
524   if (!xend) {
525     for (i=0;i<offset;i++)
526       if (!_disassemble(&last)) break;
527     memcpy(&DEBUG_LastDisassemble,&last,sizeof(last));
528     return;
529   }
530   while (last.off <= end.off)
531     if (!_disassemble(&last)) break;
532   memcpy(&DEBUG_LastDisassemble,&last,sizeof(last));
533   return;
534 }
535
536
537
538 #if 0
539 main()
540 {
541   int i, j;
542   DEBUG_AddPath("../../de");
543   while(1==1)
544     {
545       fscanf(stdin,"%d %d", &i, &j);
546       DEBUG_DisplaySource("dumpexe.c", i, j);
547     }
548   return 0;
549 }
550 #endif