1/*++
2
3Copyright (c) 2004 - 2007, Intel Corporation. All rights reserved.<BR>
4This program and the accompanying materials
5are licensed and made available under the terms and conditions of the BSD License
6which accompanies this distribution.  The full text of the license may be found at
7http://opensource.org/licenses/bsd-license.php
8
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12  Module Name:
13
14    DscFile.c
15
16  Abstract:
17
18    This module is used to process description files at a high level. For the
19    most part, it pre-parses the file to find and save off positions of all
20    the sections ([section.subsection.subsection]) in a linked list, then
21    provides services to find the sections by name, and read the lines from
22    the section until you run into the next section.
23
24  NOTE: DSC file is synonomous with section file. A DSC file is simply a file
25    containing bracketed section names [section.subsection.subsection...]
26
27--*/
28
29#include <stdio.h>  // for file ops
30#include <string.h>
31#include <ctype.h>
32#include <stdlib.h> // for malloc
33#include "Common.h"
34#include "DSCFile.h"
35
36#define MAX_INCLUDE_NEST_LEVEL  20
37
38static
39void
40DSCFileFree (
41  DSC_FILE *DSC
42  );
43
44static
45STATUS
46DSCParseInclude (
47  DSC_FILE  *DSC,
48  char      *FileName,
49  int       NestLevel
50  );
51
52//
53// Constructor for a DSC file
54//
55int
56DSCFileInit (
57  DSC_FILE *DSC
58  )
59{
60  memset ((char *) DSC, 0, sizeof (DSC_FILE));
61  DSC->SavedPositionIndex = -1;
62  return STATUS_SUCCESS;
63}
64//
65// Destructor for a DSC file
66//
67int
68DSCFileDestroy (
69  DSC_FILE *DSC
70  )
71{
72  DSC->SavedPositionIndex = -1;
73  DSCFileFree (DSC);
74  return STATUS_SUCCESS;
75}
76//
77// Get the next line from a DSC file.
78//
79char *
80DSCFileGetLine (
81  DSC_FILE  *DSC,
82  char      *Line,
83  int       LineLen
84  )
85{
86  char  *Cptr;
87
88  if (DSC->CurrentLine == NULL) {
89    return NULL;
90  }
91  //
92  // Check for running into next section
93  //
94  if (DSC->CurrentLine->Line[0] == '[') {
95    return NULL;
96  }
97  //
98  // Allow special case where the line starts with backslash-bracket. If we
99  // see this, then shift everything left one character.
100  //
101  if ((DSC->CurrentLine->Line[0] == '\\') && (DSC->CurrentLine->Line[1] == '[')) {
102    Cptr = DSC->CurrentLine->Line + 1;
103  } else {
104    Cptr = DSC->CurrentLine->Line;
105  }
106
107  strncpy (Line, Cptr, LineLen);
108  ParserSetPosition (DSC->CurrentLine->FileName, DSC->CurrentLine->LineNum);
109  DSC->CurrentLine = DSC->CurrentLine->Next;
110  return Line;
111}
112
113int
114DSCFileSetFile (
115  DSC_FILE  *DSC,
116  char      *FileName
117  )
118/*++
119
120Routine Description:
121
122  Pre-scan a section file to find all the sections. Then we can speed up
123  searching for the different sections.
124
125Arguments:
126
127  DSC       - pointer to a DSC structure (this pointer)
128  FileName  - name of the file to process
129
130Returns:
131
132  STATUS_SUCCESS if everything went well.
133
134--*/
135{
136  STATUS  Status;
137
138  //
139  // Called to open a new sectioned file.
140  //
141  Status = DSCParseInclude (DSC, FileName, 1);
142  return Status;
143}
144
145static
146STATUS
147DSCParseInclude (
148  DSC_FILE    *DSC,
149  char        *FileName,
150  int         NestLevel
151  )
152{
153  SECTION       *NewSect;
154  SECTION_LINE  *NewLine;
155  DSC_FILE_NAME *NewDscFileName;
156  char          Line[MAX_LINE_LEN];
157  char          *Start;
158  char          *End;
159  char          SaveChar;
160  char          *TempCptr;
161  char          ShortHandSectionName[MAX_LINE_LEN];
162  char          ThisSectionName[MAX_LINE_LEN];
163  SECTION       *CurrSect;
164  SECTION       *TempSect;
165  FILE          *FilePtr;
166  STATUS        Status;
167  UINT32        LineNum;
168
169  //
170  // Make sure we haven't exceeded our maximum nesting level
171  //
172  if (NestLevel > MAX_INCLUDE_NEST_LEVEL) {
173    Error (NULL, 0, 0, "application error", "maximum !include nesting level exceeded");
174    return STATUS_ERROR;
175  }
176  //
177  // Try to open the file
178  //
179  if ((FilePtr = fopen (FileName, "r")) == NULL) {
180    //
181    // This function is called to handle the DSC file from the command line too,
182    // so differentiate whether this file is an include file or the main file
183    // by examining the nest level.
184    //
185    if (NestLevel == 1) {
186      Error (NULL, 0, 0, FileName, "could not open DSC file for reading");
187    } else {
188      Error (NULL, 0, 0, FileName, "could not open !include DSC file for reading");
189    }
190
191    return STATUS_ERROR;
192  }
193  //
194  // We keep a linked list of files we parse for error reporting purposes.
195  //
196  NewDscFileName = malloc (sizeof (DSC_FILE_NAME));
197  if (NewDscFileName == NULL) {
198    Error (__FILE__, __LINE__, 0, "memory allocation failed", NULL);
199    return STATUS_ERROR;
200  }
201
202  memset (NewDscFileName, 0, sizeof (DSC_FILE_NAME));
203  NewDscFileName->FileName = (INT8 *) malloc (strlen (FileName) + 1);
204  if (NewDscFileName->FileName == NULL) {
205    Error (__FILE__, __LINE__, 0, "memory allocation failed", NULL);
206    return STATUS_ERROR;
207  }
208
209  strcpy (NewDscFileName->FileName, FileName);
210  if (DSC->FileName == NULL) {
211    DSC->FileName = NewDscFileName;
212  } else {
213    DSC->LastFileName->Next = NewDscFileName;
214  }
215
216  DSC->LastFileName = NewDscFileName;
217  //
218  // Read lines and process until done
219  //
220  Status  = STATUS_SUCCESS;
221  LineNum = 0;
222  for (;;) {
223    if (fgets (Line, sizeof (Line), FilePtr) == NULL) {
224      break;
225    }
226
227    LineNum++;
228    ParserSetPosition (FileName, LineNum);
229    //
230    // Add the line to our list if it's not a !include line
231    //
232    if ((strncmp (Line, "!include", 8) == 0) && (isspace (Line[8]))) {
233      Start = Line + 9;
234      while (*Start && (*Start != '"')) {
235        Start++;
236      }
237
238      if (*Start != '"') {
239        Error (FileName, LineNum, 0, NULL, "invalid format for !include");
240        Status = STATUS_ERROR;
241        goto Done;
242      }
243
244      Start++;
245      for (End = Start; *End && (*End != '"'); End++)
246        ;
247      if (*End != '"') {
248        Error (FileName, LineNum, 0, NULL, "invalid format for !include");
249        Status = STATUS_ERROR;
250        goto Done;
251      }
252
253      *End = 0;
254      //
255      // Expand symbols. Use 'ThisSectionName' as scratchpad
256      //
257      ExpandSymbols (Start, ThisSectionName, sizeof (ThisSectionName), EXPANDMODE_NO_UNDEFS);
258      Status = DSCParseInclude (DSC, ThisSectionName, NestLevel + 1);
259      if (Status != STATUS_SUCCESS) {
260        Error (FileName, LineNum, 0, NULL, "failed to parse !include file");
261        goto Done;
262      }
263    } else {
264      NewLine = (SECTION_LINE *) malloc (sizeof (SECTION_LINE));
265      if (NewLine == NULL) {
266        Error (NULL, 0, 0, NULL, "failed to allocate memory");
267        Status = STATUS_ERROR;
268        goto Done;
269      }
270
271      memset ((char *) NewLine, 0, sizeof (SECTION_LINE));
272      NewLine->LineNum  = LineNum;
273      NewLine->FileName = NewDscFileName->FileName;
274      NewLine->Line     = (char *) malloc (strlen (Line) + 1);
275      if (NewLine->Line == NULL) {
276        Error (NULL, 0, 0, NULL, "failed to allocate memory");
277        Status = STATUS_ERROR;
278        goto Done;
279      }
280
281      strcpy (NewLine->Line, Line);
282      if (DSC->Lines == NULL) {
283        DSC->Lines = NewLine;
284      } else {
285        DSC->LastLine->Next = NewLine;
286      }
287
288      DSC->LastLine = NewLine;
289      //
290      // Parse the line for []. Ignore [] and [----] delimiters. The
291      // line may have multiple definitions separated by commas, so
292      // take each separately
293      //
294      Start = Line;
295      if ((Line[0] == '[') && ((Line[1] != ']') && (Line[1] != '-'))) {
296        //
297        // Skip over open bracket and preceeding spaces
298        //
299        Start++;
300        ShortHandSectionName[0] = 0;
301
302        while (*Start && (*Start != ']')) {
303          while (isspace (*Start)) {
304            Start++;
305          }
306          //
307          // Hack off closing bracket or trailing spaces or comma separator.
308          // Also allow things like [section.subsection1|subsection2], which
309          // is shorthand for [section.subsection1,section.subsection2]
310          //
311          End = Start;
312          while (*End && (*End != ']') && !isspace (*End) && (*End != ',') && (*End != '|')) {
313            End++;
314          }
315          //
316          // Save the character and null-terminate the string
317          //
318          SaveChar  = *End;
319          *End      = 0;
320          //
321          // Now allocate space for a new section and add it to the linked list.
322          // If the previous section ended with the shorthand indicator, then
323          // the section name was saved off. Append this section name to it.
324          //
325          strcpy (ThisSectionName, ShortHandSectionName);
326          if (*Start == '.') {
327            strcat (ThisSectionName, Start + 1);
328          } else {
329            strcat (ThisSectionName, Start);
330          }
331          //
332          // Allocate memory for the section. Then clear it out.
333          //
334          NewSect = (SECTION *) malloc (sizeof (SECTION));
335          if (NewSect == NULL) {
336            Error (NULL, 0, 0, NULL, "failed to allocation memory for sections");
337            Status = STATUS_ERROR;
338            goto Done;
339          }
340
341          memset ((char *) NewSect, 0, sizeof (SECTION));
342          NewSect->FirstLine  = NewLine;
343          NewSect->Name       = (char *) malloc (strlen (ThisSectionName) + 1);
344          if (NewSect->Name == NULL) {
345            Error (NULL, 0, 0, NULL, "failed to allocation memory for sections");
346            Status = STATUS_ERROR;
347            goto Done;
348          }
349
350          strcpy (NewSect->Name, ThisSectionName);
351          if (DSC->Sections == NULL) {
352            DSC->Sections = NewSect;
353          } else {
354            DSC->LastSection->Next = NewSect;
355          }
356
357          DSC->LastSection  = NewSect;
358          *End              = SaveChar;
359          //
360          // If the name ended in a shorthand indicator, then save the
361          // section name and truncate it at the last dot.
362          //
363          if (SaveChar == '|') {
364            strcpy (ShortHandSectionName, ThisSectionName);
365            for (TempCptr = ShortHandSectionName + strlen (ShortHandSectionName) - 1;
366                 (TempCptr != ShortHandSectionName) && (*TempCptr != '.');
367                 TempCptr--
368                )
369              ;
370            //
371            // If we didn't find a dot, then hopefully they have [name1|name2]
372            // instead of [name1,name2].
373            //
374            if (TempCptr == ShortHandSectionName) {
375              ShortHandSectionName[0] = 0;
376            } else {
377              //
378              // Truncate after the dot
379              //
380              *(TempCptr + 1) = 0;
381            }
382          } else {
383            //
384            // Kill the shorthand string
385            //
386            ShortHandSectionName[0] = 0;
387          }
388          //
389          // Skip to next section name or closing bracket
390          //
391          while (*End && ((*End == ',') || isspace (*End) || (*End == '|'))) {
392            End++;
393          }
394
395          Start = End;
396        }
397      }
398    }
399  }
400  //
401  // Look through all the sections to make sure we don't have any duplicates.
402  // Allow [----] and [====] section separators
403  //
404  CurrSect = DSC->Sections;
405  while (CurrSect != NULL) {
406    TempSect = CurrSect->Next;
407    while (TempSect != NULL) {
408      if (isalpha (CurrSect->Name[0]) && (_stricmp (CurrSect->Name, TempSect->Name) == 0)) {
409        Error (
410          TempSect->FirstLine->FileName,
411          TempSect->FirstLine->LineNum,
412          0,
413          TempSect->Name,
414          "duplicate section found"
415          );
416        Error (
417          CurrSect->FirstLine->FileName,
418          CurrSect->FirstLine->LineNum,
419          0,
420          TempSect->Name,
421          "first definition of duplicate section"
422          );
423        Status = STATUS_ERROR;
424        goto Done;
425      }
426
427      TempSect = TempSect->Next;
428    }
429
430    CurrSect = CurrSect->Next;
431  }
432
433Done:
434  fclose (FilePtr);
435  return Status;
436}
437//
438// Free up memory allocated for DSC file handling.
439//
440static
441void
442DSCFileFree (
443  DSC_FILE *DSC
444  )
445{
446  SECTION       *NextSection;
447  SECTION_LINE  *NextLine;
448  DSC_FILE_NAME *NextName;
449
450  while (DSC->Sections != NULL) {
451    NextSection = DSC->Sections->Next;
452    if (DSC->Sections->Name != NULL) {
453      free (DSC->Sections->Name);
454    }
455
456    free (DSC->Sections);
457    DSC->Sections = NextSection;
458  }
459
460  while (DSC->Lines != NULL) {
461    NextLine = DSC->Lines->Next;
462    free (DSC->Lines->Line);
463    free (DSC->Lines);
464    DSC->Lines = NextLine;
465  }
466
467  while (DSC->FileName != NULL) {
468    NextName = DSC->FileName->Next;
469    free (DSC->FileName->FileName);
470    free (DSC->FileName);
471    DSC->FileName = NextName;
472  }
473}
474
475SECTION *
476DSCFileFindSection (
477  DSC_FILE  *DSC,
478  char      *Name
479  )
480{
481  SECTION *Sect;
482
483  //
484  // Look through all the sections to find one with this name (case insensitive)
485  //
486  Sect = DSC->Sections;
487  while (Sect != NULL) {
488    if (_stricmp (Name, Sect->Name) == 0) {
489      //
490      // Position within file
491      //
492      DSC->CurrentLine = Sect->FirstLine->Next;
493      return Sect;
494    }
495
496    Sect = Sect->Next;
497  }
498
499  return NULL;
500}
501
502int
503DSCFileSavePosition (
504  DSC_FILE *DSC
505  )
506{
507  //
508  // Advance to next slot
509  //
510  DSC->SavedPositionIndex++;
511  if (DSC->SavedPositionIndex >= MAX_SAVES) {
512    DSC->SavedPositionIndex--;
513    Error (NULL, 0, 0, "APP ERROR", "max nesting of saved section file positions exceeded");
514    return STATUS_ERROR;
515  }
516
517  DSC->SavedPosition[DSC->SavedPositionIndex] = DSC->CurrentLine;
518  return STATUS_SUCCESS;
519}
520
521int
522DSCFileRestorePosition (
523  DSC_FILE *DSC
524  )
525{
526  if (DSC->SavedPositionIndex < 0) {
527    Error (NULL, 0, 0, "APP ERROR", "underflow of saved positions in section file");
528    return STATUS_ERROR;
529  }
530
531  DSC->CurrentLine = DSC->SavedPosition[DSC->SavedPositionIndex];
532  DSC->SavedPositionIndex--;
533  return STATUS_SUCCESS;
534}
535