• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2 
3 Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution.  The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8 
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11 
12 
13 **/
14 
15 #include "Edb.h"
16 
17 /**
18   Set the current coordinates of the cursor position.
19 
20   @param  ConOut        Point to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
21   @param  Column        The position to set the cursor to.
22   @param  Row           The position to set the cursor to.
23   @param  LineLength    Length of a line.
24   @param  TotalRow      Total row of a screen.
25   @param  Str           Point to the string.
26   @param  StrPos        The position of the string.
27   @param  Len           The length of the string.
28 
29 **/
30 VOID
31 EFIAPI
32 SetCursorPosition (
33   IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *ConOut,
34   IN  UINTN                           Column,
35   IN  INTN                            Row,
36   IN  UINTN                           LineLength,
37   IN  UINTN                           TotalRow,
38   IN  CHAR16                          *Str,
39   IN  UINTN                           StrPos,
40   IN  UINTN                           Len
41   );
42 
43 /**
44 
45   Function waits for a given event to fire, or for an optional timeout to expire.
46 
47   @param  Event            - The event to wait for
48   @param  Timeout          - An optional timeout value in 100 ns units.
49 
50   @retval EFI_SUCCESS       - Event fired before Timeout expired.
51   @retval EFI_TIME_OUT     - Timout expired before Event fired..
52 
53 **/
54 EFI_STATUS
55 EFIAPI
WaitForSingleEvent(IN EFI_EVENT Event,IN UINT64 Timeout OPTIONAL)56 WaitForSingleEvent (
57   IN EFI_EVENT                  Event,
58   IN UINT64                     Timeout OPTIONAL
59   )
60 {
61   EFI_STATUS  Status;
62   UINTN       Index;
63   EFI_EVENT   TimerEvent;
64   EFI_EVENT   WaitList[2];
65 
66   if (Timeout != 0) {
67     //
68     // Create a timer event
69     //
70     Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
71     if (!EFI_ERROR (Status)) {
72       //
73       // Set the timer event
74       //
75       gBS->SetTimer (
76             TimerEvent,
77             TimerRelative,
78             Timeout
79             );
80 
81       //
82       // Wait for the original event or the timer
83       //
84       WaitList[0] = Event;
85       WaitList[1] = TimerEvent;
86       Status      = gBS->WaitForEvent (2, WaitList, &Index);
87       gBS->CloseEvent (TimerEvent);
88 
89       //
90       // If the timer expired, change the return to timed out
91       //
92       if (!EFI_ERROR (Status) && Index == 1) {
93         Status = EFI_TIMEOUT;
94       }
95     }
96   } else {
97     //
98     // No timeout... just wait on the event
99     //
100     Status = gBS->WaitForEvent (1, &Event, &Index);
101     ASSERT (!EFI_ERROR (Status));
102     ASSERT (Index == 0);
103   }
104 
105   return Status;
106 }
107 
108 /**
109 
110   Move the cursor position one character backward.
111 
112   @param  LineLength       Length of a line. Get it by calling QueryMode
113   @param  Column           Current column of the cursor position
114   @param  Row              Current row of the cursor position
115 
116 **/
117 VOID
118 EFIAPI
ConMoveCursorBackward(IN UINTN LineLength,IN OUT UINTN * Column,IN OUT UINTN * Row)119 ConMoveCursorBackward (
120   IN     UINTN                   LineLength,
121   IN OUT UINTN                   *Column,
122   IN OUT UINTN                   *Row
123   )
124 {
125   ASSERT (Column != NULL);
126   ASSERT (Row != NULL);
127   //
128   // If current column is 0, move to the last column of the previous line,
129   // otherwise, just decrement column.
130   //
131   if (*Column == 0) {
132     (*Column) = LineLength - 1;
133     //
134     //   if (*Row > 0) {
135     //
136     (*Row)--;
137     //
138     // }
139     //
140   } else {
141     (*Column)--;
142   }
143 }
144 
145 /**
146 
147   Move the cursor position one character backward.
148 
149   @param  LineLength       Length of a line. Get it by calling QueryMode
150   @param  TotalRow         Total row of a screen, get by calling QueryMode
151   @param  Column           Current column of the cursor position
152   @param  Row              Current row of the cursor position
153 
154 **/
155 VOID
156 EFIAPI
ConMoveCursorForward(IN UINTN LineLength,IN UINTN TotalRow,IN OUT UINTN * Column,IN OUT UINTN * Row)157 ConMoveCursorForward (
158   IN     UINTN                   LineLength,
159   IN     UINTN                   TotalRow,
160   IN OUT UINTN                   *Column,
161   IN OUT UINTN                   *Row
162   )
163 {
164   ASSERT (Column != NULL);
165   ASSERT (Row != NULL);
166   //
167   // If current column is at line end, move to the first column of the nest
168   // line, otherwise, just increment column.
169   //
170   (*Column)++;
171   if (*Column >= LineLength) {
172     (*Column) = 0;
173     if ((*Row) < TotalRow - 1) {
174       (*Row)++;
175     }
176   }
177 }
178 
179 CHAR16 mBackupSpace[EFI_DEBUG_INPUS_BUFFER_SIZE];
180 CHAR16 mInputBufferHistory[EFI_DEBUG_INPUS_BUFFER_SIZE];
181 
182 /**
183 
184   Get user input.
185 
186   @param  Prompt       The prompt string.
187   @param  InStr        Point to the input string.
188   @param  StrLength    The max length of string user can input.
189 
190 **/
191 VOID
192 EFIAPI
Input(IN CHAR16 * Prompt OPTIONAL,OUT CHAR16 * InStr,IN UINTN StrLength)193 Input (
194   IN CHAR16    *Prompt OPTIONAL,
195   OUT CHAR16   *InStr,
196   IN UINTN     StrLength
197   )
198 {
199   EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL     *ConOut;
200   EFI_SIMPLE_TEXT_INPUT_PROTOCOL      *ConIn;
201   BOOLEAN       Done;
202   UINTN         Column;
203   UINTN         Row;
204   UINTN         StartColumn;
205   UINTN         Update;
206   UINTN         Delete;
207   UINTN         Len;
208   UINTN         StrPos;
209   UINTN         Index;
210   UINTN         LineLength;
211   UINTN         TotalRow;
212   UINTN         SkipLength;
213   UINTN         OutputLength;
214   UINTN         TailRow;
215   UINTN         TailColumn;
216   EFI_INPUT_KEY Key;
217   BOOLEAN       InsertMode;
218   BOOLEAN       NeedAdjust;
219   UINTN         SubIndex;
220   CHAR16        *CommandStr;
221 
222   ConOut = gST->ConOut;
223   ConIn = gST->ConIn;
224 
225   ASSERT (ConOut != NULL);
226   ASSERT (ConIn != NULL);
227   ASSERT (InStr != NULL);
228 
229   if (Prompt != NULL) {
230     ConOut->OutputString (ConOut, Prompt);
231   }
232   //
233   // Read a line from the console
234   //
235   Len           = 0;
236   StrPos        = 0;
237   OutputLength  = 0;
238   Update        = 0;
239   Delete        = 0;
240   InsertMode    = TRUE;
241   NeedAdjust    = FALSE;
242 
243   //
244   // If buffer is not large enough to hold a CHAR16, do nothing.
245   //
246   if (StrLength < 1) {
247     return ;
248   }
249   //
250   // Get the screen setting and the current cursor location
251   //
252   StartColumn = ConOut->Mode->CursorColumn;
253   Column      = StartColumn;
254   Row         = ConOut->Mode->CursorRow;
255   ConOut->QueryMode (ConOut, ConOut->Mode->Mode, &LineLength, &TotalRow);
256   if (LineLength == 0) {
257     return ;
258   }
259 
260   SetMem (InStr, StrLength * sizeof (CHAR16), 0);
261   Done = FALSE;
262   do {
263     //
264     // Read a key
265     //
266     WaitForSingleEvent (ConIn->WaitForKey, 0);
267     ConIn->ReadKeyStroke (ConIn, &Key);
268 
269     switch (Key.UnicodeChar) {
270     case CHAR_CARRIAGE_RETURN:
271       //
272       // All done, print a newline at the end of the string
273       //
274       TailRow     = Row + (Len - StrPos + Column) / LineLength;
275       TailColumn  = (Len - StrPos + Column) % LineLength;
276       Done        = TRUE;
277       break;
278 
279     case CHAR_BACKSPACE:
280       if (StrPos != 0) {
281         //
282         // If not move back beyond string beginning, move all characters behind
283         // the current position one character forward
284         //
285         StrPos -= 1;
286         Update  = StrPos;
287         Delete  = 1;
288         CopyMem (InStr + StrPos, InStr + StrPos + 1, sizeof (CHAR16) * (Len - StrPos));
289 
290         //
291         // Adjust the current column and row
292         //
293         ConMoveCursorBackward (LineLength, &Column, &Row);
294 
295         NeedAdjust = TRUE;
296       }
297       break;
298 
299     default:
300       if (Key.UnicodeChar >= ' ') {
301         //
302         // If we are at the buffer's end, drop the key
303         //
304         if (Len == StrLength - 1 && (InsertMode || StrPos == Len)) {
305           break;
306         }
307         //
308         // If in insert mode, move all characters behind the current position
309         // one character backward to make space for this character. Then store
310         // the character.
311         //
312         if (InsertMode) {
313           for (Index = Len; Index > StrPos; Index -= 1) {
314             InStr[Index] = InStr[Index - 1];
315           }
316         }
317 
318         InStr[StrPos] = Key.UnicodeChar;
319         Update        = StrPos;
320 
321         StrPos += 1;
322         OutputLength = 1;
323       }
324       break;
325 
326     case 0:
327       switch (Key.ScanCode) {
328       case SCAN_DELETE:
329         //
330         // Move characters behind current position one character forward
331         //
332         if (Len != 0) {
333           Update  = StrPos;
334           Delete  = 1;
335           CopyMem (InStr + StrPos, InStr + StrPos + 1, sizeof (CHAR16) * (Len - StrPos));
336 
337           NeedAdjust = TRUE;
338         }
339         break;
340 
341       case SCAN_LEFT:
342         //
343         // Adjust current cursor position
344         //
345         if (StrPos != 0) {
346           StrPos -= 1;
347           ConMoveCursorBackward (LineLength, &Column, &Row);
348         }
349         break;
350 
351       case SCAN_RIGHT:
352         //
353         // Adjust current cursor position
354         //
355         if (StrPos < Len) {
356           StrPos += 1;
357           ConMoveCursorForward (LineLength, TotalRow, &Column, &Row);
358         }
359         break;
360 
361       case SCAN_HOME:
362         //
363         // Move current cursor position to the beginning of the command line
364         //
365         Row -= (StrPos + StartColumn) / LineLength;
366         Column  = StartColumn;
367         StrPos  = 0;
368         break;
369 
370       case SCAN_END:
371         //
372         // Move current cursor position to the end of the command line
373         //
374         TailRow     = Row + (Len - StrPos + Column) / LineLength;
375         TailColumn  = (Len - StrPos + Column) % LineLength;
376         Row         = TailRow;
377         Column      = TailColumn;
378         StrPos      = Len;
379         break;
380 
381       case SCAN_ESC:
382         //
383         // Prepare to clear the current command line
384         //
385         InStr[0]  = 0;
386         Update    = 0;
387         Delete    = Len;
388         Row -= (StrPos + StartColumn) / LineLength;
389         Column        = StartColumn;
390         OutputLength  = 0;
391 
392         NeedAdjust = TRUE;
393         break;
394 
395       case SCAN_INSERT:
396         //
397         // Toggle the SEnvInsertMode flag
398         //
399         InsertMode = (BOOLEAN)!InsertMode;
400         break;
401 
402       case SCAN_UP:
403       case SCAN_DOWN:
404         //
405         // show history
406         //
407         CopyMem (InStr, mInputBufferHistory, StrLength * sizeof(CHAR16));
408         StrPos       = StrLen (mInputBufferHistory);
409         Update       = 0;
410         Delete       = 0;
411         OutputLength = 0;
412 
413         TailRow      = Row + (StrPos + StartColumn) / LineLength;
414         TailColumn   = (StrPos + StartColumn) % LineLength;
415         Row          = TailRow;
416         Column       = TailColumn;
417         NeedAdjust   = FALSE;
418 
419         ConOut->SetCursorPosition (ConOut, StartColumn, Row);
420         for (SubIndex = 0; SubIndex < EFI_DEBUG_INPUS_BUFFER_SIZE - (StartColumn - EFI_DEBUG_PROMPT_COLUMN); SubIndex++) {
421           mBackupSpace[SubIndex] = L' ';
422         }
423         EDBPrint (mBackupSpace);
424         SetMem (mBackupSpace, (EFI_DEBUG_INPUS_BUFFER_SIZE - (StartColumn - EFI_DEBUG_PROMPT_COLUMN)) * sizeof(CHAR16), 0);
425 
426         ConOut->SetCursorPosition (ConOut, StartColumn, Row);
427         Len = StrPos;
428 
429         break;
430 
431       case SCAN_F1:
432       case SCAN_F2:
433       case SCAN_F3:
434       case SCAN_F4:
435       case SCAN_F5:
436       case SCAN_F6:
437       case SCAN_F7:
438       case SCAN_F8:
439       case SCAN_F9:
440       case SCAN_F10:
441       case SCAN_F11:
442       case SCAN_F12:
443         CommandStr = GetCommandNameByKey (Key);
444         if (CommandStr != NULL) {
445           StrnCpyS (InStr, StrLength, CommandStr, StrLength - 1);
446           return ;
447         }
448         break;
449       }
450     }
451 
452     if (Done) {
453       break;
454     }
455     //
456     // If we need to update the output do so now
457     //
458     if (Update != -1) {
459       if (NeedAdjust) {
460         ConOut->SetCursorPosition (ConOut, Column, Row);
461         for (SubIndex = 0; SubIndex < EFI_DEBUG_INPUS_BUFFER_SIZE - (Column - EFI_DEBUG_PROMPT_COLUMN); SubIndex++) {
462           mBackupSpace[SubIndex] = L' ';
463         }
464         EDBPrint (mBackupSpace);
465         SetMem (mBackupSpace, (EFI_DEBUG_INPUS_BUFFER_SIZE - (Column - EFI_DEBUG_PROMPT_COLUMN)) * sizeof(CHAR16), 0);
466         ConOut->SetCursorPosition (ConOut, Column, Row);
467         NeedAdjust = FALSE;
468       }
469       EDBPrint (InStr + Update);
470       Len = StrLen (InStr);
471 
472       if (Delete != 0) {
473         SetMem (InStr + Len, Delete * sizeof (CHAR16), 0x00);
474       }
475 
476       if (StrPos > Len) {
477         StrPos = Len;
478       }
479 
480       Update = (UINTN) -1;
481 
482       //
483       // After using print to reflect newly updates, if we're not using
484       // BACKSPACE and DELETE, we need to move the cursor position forward,
485       // so adjust row and column here.
486       //
487       if (Key.UnicodeChar != CHAR_BACKSPACE && !(Key.UnicodeChar == 0 && Key.ScanCode == SCAN_DELETE)) {
488         //
489         // Calulate row and column of the tail of current string
490         //
491         TailRow     = Row + (Len - StrPos + Column + OutputLength) / LineLength;
492         TailColumn  = (Len - StrPos + Column + OutputLength) % LineLength;
493 
494         //
495         // If the tail of string reaches screen end, screen rolls up, so if
496         // Row does not equal TailRow, Row should be decremented
497         //
498         // (if we are recalling commands using UPPER and DOWN key, and if the
499         // old command is too long to fit the screen, TailColumn must be 79.
500         //
501         if (TailColumn == 0 && TailRow >= TotalRow && (UINTN) Row != TailRow) {
502           Row--;
503         }
504         //
505         // Calculate the cursor position after current operation. If cursor
506         // reaches line end, update both row and column, otherwise, only
507         // column will be changed.
508         //
509         if (Column + OutputLength >= LineLength) {
510           SkipLength = OutputLength - (LineLength - Column);
511 
512           Row += SkipLength / LineLength + 1;
513           if ((UINTN) Row > TotalRow - 1) {
514             Row = TotalRow - 1;
515           }
516 
517           Column = SkipLength % LineLength;
518         } else {
519           Column += OutputLength;
520         }
521       }
522 
523       Delete = 0;
524     }
525     //
526     // Set the cursor position for this key
527     //
528     SetCursorPosition (ConOut, Column, Row, LineLength, TotalRow, InStr, StrPos, Len);
529   } while (!Done);
530 
531   CopyMem (mInputBufferHistory, InStr, StrLength * sizeof(CHAR16));
532 
533   //
534   // Return the data to the caller
535   //
536   return ;
537 }
538 
539 /**
540   Set the current coordinates of the cursor position.
541 
542   @param  ConOut        Point to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
543   @param  Column        The position to set the cursor to.
544   @param  Row           The position to set the cursor to.
545   @param  LineLength    Length of a line.
546   @param  TotalRow      Total row of a screen.
547   @param  Str           Point to the string.
548   @param  StrPos        The position of the string.
549   @param  Len           The length of the string.
550 
551 **/
552 VOID
553 EFIAPI
SetCursorPosition(IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL * ConOut,IN UINTN Column,IN INTN Row,IN UINTN LineLength,IN UINTN TotalRow,IN CHAR16 * Str,IN UINTN StrPos,IN UINTN Len)554 SetCursorPosition (
555   IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut,
556   IN  UINTN                           Column,
557   IN  INTN                            Row,
558   IN  UINTN                           LineLength,
559   IN  UINTN                           TotalRow,
560   IN  CHAR16                          *Str,
561   IN  UINTN                           StrPos,
562   IN  UINTN                           Len
563   )
564 {
565   CHAR16  Backup;
566 
567   ASSERT (ConOut != NULL);
568   ASSERT (Str != NULL);
569 
570   Backup = 0;
571   if (Row >= 0) {
572     ConOut->SetCursorPosition (ConOut, Column, Row);
573     return ;
574   }
575 
576   if (Len - StrPos > Column * Row) {
577     Backup                          = *(Str + StrPos + Column * Row);
578     *(Str + StrPos + Column * Row)  = 0;
579   }
580 
581   EDBPrint (L"%s", Str + StrPos);
582   if (Len - StrPos > Column * Row) {
583     *(Str + StrPos + Column * Row) = Backup;
584   }
585 
586   ConOut->SetCursorPosition (ConOut, 0, 0);
587 }
588 
589 /**
590 
591   SetPageBreak.
592 
593 **/
594 BOOLEAN
595 EFIAPI
SetPageBreak(VOID)596 SetPageBreak (
597   VOID
598   )
599 {
600   EFI_INPUT_KEY Key;
601   CHAR16        Str[3];
602   BOOLEAN       OmitPrint;
603 
604   //
605   // Check
606   //
607   if (!mDebuggerPrivate.EnablePageBreak) {
608     return FALSE;
609   }
610 
611   gST->ConOut->OutputString (gST->ConOut, L"Press ENTER to continue, 'q' to exit:");
612 
613   OmitPrint = FALSE;
614   //
615   // Wait for user input
616   //
617   Str[0]  = ' ';
618   Str[1]  = 0;
619   Str[2]  = 0;
620   for (;;) {
621     WaitForSingleEvent (gST->ConIn->WaitForKey, 0);
622     gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
623 
624     //
625     // handle control keys
626     //
627     if (Key.UnicodeChar == CHAR_NULL) {
628       if (Key.ScanCode == SCAN_ESC) {
629         gST->ConOut->OutputString (gST->ConOut, L"\r\n");
630         OmitPrint = TRUE;
631         break;
632       }
633 
634       continue;
635     }
636 
637     if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
638       gST->ConOut->OutputString (gST->ConOut, L"\r\n");
639       break;
640     }
641     //
642     // Echo input
643     //
644     Str[1] = Key.UnicodeChar;
645     if (Str[1] == CHAR_BACKSPACE) {
646       continue;
647     }
648 
649     gST->ConOut->OutputString (gST->ConOut, Str);
650 
651     if ((Str[1] == L'q') || (Str[1] == L'Q')) {
652       OmitPrint = TRUE;
653     } else {
654       OmitPrint = FALSE;
655     }
656 
657     Str[0] = CHAR_BACKSPACE;
658   }
659 
660   return OmitPrint;
661 }
662 
663 /**
664   Print a Unicode string to the output device.
665 
666   @param  Format    A Null-terminated Unicode format string.
667   @param  ...       The variable argument list that contains pointers to Null-
668                     terminated Unicode strings to be printed
669 
670 **/
671 UINTN
672 EFIAPI
EDBPrint(IN CONST CHAR16 * Format,...)673 EDBPrint (
674   IN CONST CHAR16  *Format,
675   ...
676   )
677 {
678   UINTN   Return;
679   VA_LIST Marker;
680   CHAR16  Buffer[EFI_DEBUG_MAX_PRINT_BUFFER];
681 
682   VA_START (Marker, Format);
683   Return = UnicodeVSPrint (Buffer, sizeof (Buffer), Format, Marker);
684   VA_END (Marker);
685 
686   if (gST->ConOut != NULL) {
687     //
688     // To be extra safe make sure ConOut has been initialized
689     //
690     gST->ConOut->OutputString (gST->ConOut, Buffer);
691   }
692 
693   return Return;
694 }
695 
696 /**
697   Print a Unicode string to the output buffer.
698 
699   @param  Buffer          A pointer to the output buffer for the produced Null-terminated
700                           Unicode string.
701   @param  BufferSize      The size, in bytes, of the output buffer specified by StartOfBuffer.
702   @param  Format          A Null-terminated Unicode format string.
703   @param  ...             The variable argument list that contains pointers to Null-
704                           terminated Unicode strings to be printed
705 
706 **/
707 UINTN
708 EFIAPI
EDBSPrint(OUT CHAR16 * Buffer,IN INTN BufferSize,IN CONST CHAR16 * Format,...)709 EDBSPrint (
710   OUT CHAR16        *Buffer,
711   IN  INTN          BufferSize,
712   IN  CONST CHAR16  *Format,
713   ...
714   )
715 {
716   UINTN   Return;
717   VA_LIST Marker;
718 
719   ASSERT (BufferSize > 0);
720 
721   VA_START (Marker, Format);
722   Return = UnicodeVSPrint (Buffer, (UINTN)BufferSize, Format, Marker);
723   VA_END (Marker);
724 
725   return Return;
726 }
727 
728 /**
729   Print a Unicode string to the output buffer with specified offset..
730 
731   @param  Buffer          A pointer to the output buffer for the produced Null-terminated
732                           Unicode string.
733   @param  BufferSize      The size, in bytes, of the output buffer specified by StartOfBuffer.
734   @param  Offset          The offset of the buffer.
735   @param  Format          A Null-terminated Unicode format string.
736   @param  ...             The variable argument list that contains pointers to Null-
737                           terminated Unicode strings to be printed
738 
739 **/
740 UINTN
741 EFIAPI
EDBSPrintWithOffset(OUT CHAR16 * Buffer,IN INTN BufferSize,IN UINTN Offset,IN CONST CHAR16 * Format,...)742 EDBSPrintWithOffset (
743   OUT CHAR16        *Buffer,
744   IN  INTN          BufferSize,
745   IN  UINTN         Offset,
746   IN  CONST CHAR16  *Format,
747   ...
748   )
749 {
750   UINTN   Return;
751   VA_LIST Marker;
752 
753   ASSERT (BufferSize - (Offset * sizeof(CHAR16)) > 0);
754 
755   VA_START (Marker, Format);
756   Return = UnicodeVSPrint (Buffer + Offset, (UINTN)(BufferSize - (Offset * sizeof(CHAR16))), Format, Marker);
757   VA_END (Marker);
758 
759   return Return;
760 }
761