xref: /reactos/sdk/lib/cmlib/cmlib.h (revision 586bea13)
1 /*
2  * PROJECT:     ReactOS Kernel
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Configuration Manager Library - CMLIB header
5  * COPYRIGHT:   Copyright 2001 - 2005 Eric Kohl
6  *              Copyright 2022 George Bișoc <george.bisoc@reactos.org>
7  */
8 
9 #ifndef _CMLIB_H_
10 #define _CMLIB_H_
11 
12 //
13 // Debug support switch
14 //
15 #define _CMLIB_DEBUG_ 1
16 
17 #ifdef CMLIB_HOST
18     #include <typedefs.h>
19     #include <stdio.h>
20     #include <string.h>
21 
22     // NTDDI_xxx versions we allude to in the library (see psdk/sdkddkver.h)
23     #define NTDDI_WS03SP4                       0x05020400
24     #define NTDDI_WIN6                          0x06000000
25     #define NTDDI_LONGHORN                      NTDDI_WIN6
26     #define NTDDI_VISTA                         NTDDI_WIN6
27     #define NTDDI_WIN7                          0x06010000
28 
29     #define NTDDI_VERSION   NTDDI_WS03SP4 // This is the ReactOS NT kernel version
30 
31     /* C_ASSERT Definition */
32     #define C_ASSERT(expr) extern char (*c_assert(void)) [(expr) ? 1 : -1]
33 
34     #ifdef _WIN32
35     #define strncasecmp _strnicmp
36     #define strcasecmp _stricmp
37     #endif // _WIN32
38 
39     #if (!defined(_MSC_VER) || (_MSC_VER < 1500))
40     #define _In_
41     #define _Out_
42     #define _Inout_
43     #define _In_opt_
44     #define _In_range_(x, y)
45     #endif
46 
47     #define __drv_aliasesMem
48 
49     #ifndef min
50     #define min(a, b)  (((a) < (b)) ? (a) : (b))
51     #endif
52 
53     // #ifndef max
54     // #define max(a, b)  (((a) > (b)) ? (a) : (b))
55     // #endif
56 
57     // Definitions copied from <ntstatus.h>
58     // We only want to include host headers, so we define them manually
59     #define STATUS_SUCCESS                   ((NTSTATUS)0x00000000)
60     #define STATUS_NOT_IMPLEMENTED           ((NTSTATUS)0xC0000002)
61     #define STATUS_NO_MEMORY                 ((NTSTATUS)0xC0000017)
62     #define STATUS_INSUFFICIENT_RESOURCES    ((NTSTATUS)0xC000009A)
63     #define STATUS_INVALID_PARAMETER         ((NTSTATUS)0xC000000D)
64     #define STATUS_REGISTRY_CORRUPT          ((NTSTATUS)0xC000014C)
65     #define STATUS_REGISTRY_IO_FAILED        ((NTSTATUS)0xC000014D)
66     #define STATUS_NOT_REGISTRY_FILE         ((NTSTATUS)0xC000015C)
67     #define STATUS_REGISTRY_RECOVERED        ((NTSTATUS)0x40000009)
68 
69     #define REG_OPTION_VOLATILE              1
70     #define OBJ_CASE_INSENSITIVE             0x00000040L
71     #define USHORT_MAX                       USHRT_MAX
72 
73     #define OBJ_NAME_PATH_SEPARATOR          ((WCHAR)L'\\')
74     #define UNICODE_NULL                     ((WCHAR)0)
75 
76     VOID NTAPI
77     RtlInitUnicodeString(
78         IN OUT PUNICODE_STRING DestinationString,
79         IN PCWSTR SourceString);
80 
81     LONG NTAPI
82     RtlCompareUnicodeString(
83         IN PCUNICODE_STRING String1,
84         IN PCUNICODE_STRING String2,
85         IN BOOLEAN CaseInSensitive);
86 
87     // FIXME: DECLSPEC_NORETURN
88     VOID
89     NTAPI
90     KeBugCheckEx(
91         IN ULONG BugCheckCode,
92         IN ULONG_PTR BugCheckParameter1,
93         IN ULONG_PTR BugCheckParameter2,
94         IN ULONG_PTR BugCheckParameter3,
95         IN ULONG_PTR BugCheckParameter4);
96 
97     VOID NTAPI
98     KeQuerySystemTime(
99         OUT PLARGE_INTEGER CurrentTime);
100 
101     WCHAR NTAPI
102     RtlUpcaseUnicodeChar(
103         IN WCHAR Source);
104 
105     VOID NTAPI
106     RtlInitializeBitMap(
107         IN PRTL_BITMAP BitMapHeader,
108         IN PULONG BitMapBuffer,
109         IN ULONG SizeOfBitMap);
110 
111     ULONG NTAPI
112     RtlFindSetBits(
113         IN PRTL_BITMAP BitMapHeader,
114         IN ULONG NumberToFind,
115         IN ULONG HintIndex);
116 
117     VOID NTAPI
118     RtlSetBits(
119         IN PRTL_BITMAP BitMapHeader,
120         IN ULONG StartingIndex,
121         IN ULONG NumberToSet);
122 
123     VOID NTAPI
124     RtlClearAllBits(
125         IN PRTL_BITMAP BitMapHeader);
126 
127     #define RtlCheckBit(BMH,BP) (((((PLONG)(BMH)->Buffer)[(BP) / 32]) >> ((BP) % 32)) & 0x1)
128     #define UNREFERENCED_PARAMETER(P) ((void)(P))
129 
130     #define PKTHREAD PVOID
131     #define PKGUARDED_MUTEX PVOID
132     #define PERESOURCE PVOID
133     #define PFILE_OBJECT PVOID
134     #define PKEVENT PVOID
135     #define PWORK_QUEUE_ITEM PVOID
136     #define EX_PUSH_LOCK PULONG_PTR
137 
138     // Definitions copied from <ntifs.h>
139     // We only want to include host headers, so we define them manually
140 
141     typedef USHORT SECURITY_DESCRIPTOR_CONTROL, *PSECURITY_DESCRIPTOR_CONTROL;
142 
143     typedef struct _SECURITY_DESCRIPTOR_RELATIVE
144     {
145         UCHAR Revision;
146         UCHAR Sbz1;
147         SECURITY_DESCRIPTOR_CONTROL Control;
148         ULONG Owner;
149         ULONG Group;
150         ULONG Sacl;
151         ULONG Dacl;
152     } SECURITY_DESCRIPTOR_RELATIVE, *PISECURITY_DESCRIPTOR_RELATIVE;
153 
154     #define CMLTRACE(x, ...)
155     #undef PAGED_CODE
156     #define PAGED_CODE()
157     #define REGISTRY_ERROR                   ((ULONG)0x00000051L)
158 
159 #else
160 
161     //
162     // Debug/Tracing support
163     //
164     #if _CMLIB_DEBUG_
165     #ifdef NEW_DEBUG_SYSTEM_IMPLEMENTED // enable when Debug Filters are implemented
166     #define CMLTRACE DbgPrintEx
167     #else
168     #define CMLTRACE(x, ...)                                 \
169     if (x & CmlibTraceLevel) DbgPrint(__VA_ARGS__)
170     #endif
171     #else
172     #define CMLTRACE(x, ...) DPRINT(__VA_ARGS__)
173     #endif
174 
175     #include <ntdef.h>
176     #include <ntifs.h>
177     #include <bugcodes.h>
178 
179     /* Prevent inclusion of Windows headers through <wine/unicode.h> */
180     #define _WINDEF_
181     #define _WINBASE_
182     #define _WINNLS_
183 #endif
184 
185 
186 //
187 // These define the Debug Masks Supported
188 //
189 #define CMLIB_HCELL_DEBUG       0x01
190 
191 #ifndef ROUND_UP
192 #define ROUND_UP(a,b)        ((((a)+(b)-1)/(b))*(b))
193 #define ROUND_DOWN(a,b)      (((a)/(b))*(b))
194 #endif
195 
196 //
197 // PAGE_SIZE definition
198 //
199 #ifndef PAGE_SIZE
200 #if defined(TARGET_i386) || defined(TARGET_amd64) || \
201     defined(TARGET_arm)  || defined(TARGET_arm64)
202 #define PAGE_SIZE 0x1000
203 #else
204 #error Local PAGE_SIZE definition required when built as host
205 #endif
206 #endif
207 
208 #define TAG_CM     '  MC'
209 #define TAG_KCB    'bkMC'
210 #define TAG_CMHIVE 'vHMC'
211 #define TAG_CMSD   'DSMC'
212 
213 #define CMAPI NTAPI
214 
215 #include <wine/unicode.h>
216 #include <wchar.h>
217 #include "hivedata.h"
218 #include "cmdata.h"
219 
220 /* Forward declarations */
221 typedef struct _CM_KEY_SECURITY_CACHE_ENTRY *PCM_KEY_SECURITY_CACHE_ENTRY;
222 typedef struct _CM_KEY_CONTROL_BLOCK *PCM_KEY_CONTROL_BLOCK;
223 typedef struct _CM_CELL_REMAP_BLOCK *PCM_CELL_REMAP_BLOCK;
224 
225 // See ntoskrnl/include/internal/cm.h
226 #define CMP_SECURITY_HASH_LISTS     64
227 
228 //
229 // Use Count Log and Entry
230 //
231 typedef struct _CM_USE_COUNT_LOG_ENTRY
232 {
233     HCELL_INDEX Cell;
234     PVOID Stack[7];
235 } CM_USE_COUNT_LOG_ENTRY, *PCM_USE_COUNT_LOG_ENTRY;
236 
237 typedef struct _CM_USE_COUNT_LOG
238 {
239     USHORT Next;
240     USHORT Size;
241     CM_USE_COUNT_LOG_ENTRY Log[32];
242 } CM_USE_COUNT_LOG, *PCM_USE_COUNT_LOG;
243 
244 //
245 // Configuration Manager Hive Structure
246 //
247 typedef struct _CMHIVE
248 {
249     HHIVE Hive;
250     HANDLE FileHandles[HFILE_TYPE_MAX];
251     LIST_ENTRY NotifyList;
252     LIST_ENTRY HiveList;
253     EX_PUSH_LOCK HiveLock;
254     PKTHREAD HiveLockOwner;
255     PKGUARDED_MUTEX ViewLock;
256     PKTHREAD ViewLockOwner;
257     EX_PUSH_LOCK WriterLock;
258     PKTHREAD WriterLockOwner;
259     PERESOURCE FlusherLock;
260     EX_PUSH_LOCK SecurityLock;
261     PKTHREAD HiveSecurityLockOwner;
262     LIST_ENTRY LRUViewListHead;
263     LIST_ENTRY PinViewListHead;
264     PFILE_OBJECT FileObject;
265     UNICODE_STRING FileFullPath;
266     UNICODE_STRING FileUserName;
267     USHORT MappedViews;
268     USHORT PinnedViews;
269     ULONG UseCount;
270     ULONG SecurityCount;
271     ULONG SecurityCacheSize;
272     LONG SecurityHitHint;
273     PCM_KEY_SECURITY_CACHE_ENTRY SecurityCache;
274     LIST_ENTRY SecurityHash[CMP_SECURITY_HASH_LISTS];
275     PKEVENT UnloadEvent;
276     PCM_KEY_CONTROL_BLOCK RootKcb;
277     BOOLEAN Frozen;
278     PWORK_QUEUE_ITEM UnloadWorkItem;
279     BOOLEAN GrowOnlyMode;
280     ULONG GrowOffset;
281     LIST_ENTRY KcbConvertListHead;
282     LIST_ENTRY KnodeConvertListHead;
283     PCM_CELL_REMAP_BLOCK CellRemapArray;
284     CM_USE_COUNT_LOG UseCountLog;
285     CM_USE_COUNT_LOG LockHiveLog;
286     ULONG Flags;
287     LIST_ENTRY TrustClassEntry;
288     ULONG FlushCount;
289     BOOLEAN HiveIsLoading;
290     PKTHREAD CreatorOwner;
291 } CMHIVE, *PCMHIVE;
292 
293 typedef struct _HV_HIVE_CELL_PAIR
294 {
295     PHHIVE Hive;
296     HCELL_INDEX Cell;
297 } HV_HIVE_CELL_PAIR, *PHV_HIVE_CELL_PAIR;
298 
299 #define STATIC_CELL_PAIR_COUNT 4
300 typedef struct _HV_TRACK_CELL_REF
301 {
302     USHORT Count;
303     USHORT Max;
304     PHV_HIVE_CELL_PAIR CellArray;
305     HV_HIVE_CELL_PAIR StaticArray[STATIC_CELL_PAIR_COUNT];
306     USHORT StaticCount;
307 } HV_TRACK_CELL_REF, *PHV_TRACK_CELL_REF;
308 
309 extern ULONG CmlibTraceLevel;
310 
311 //
312 // Hack since big keys are not yet supported
313 //
314 #define ASSERT_VALUE_BIG(h, s)  \
315     ASSERTMSG("Big keys not supported!\n", !CmpIsKeyValueBig(h, s));
316 
317 //
318 // Returns whether or not this is a small valued key
319 //
320 static inline
321 BOOLEAN
322 CmpIsKeyValueSmall(OUT PULONG RealLength,
323                    IN ULONG Length)
324 {
325     /* Check if the length has the special size value */
326     if (Length >= CM_KEY_VALUE_SPECIAL_SIZE)
327     {
328         /* It does, so this is a small key: return the real length */
329         *RealLength = Length - CM_KEY_VALUE_SPECIAL_SIZE;
330         return TRUE;
331     }
332 
333     /* This is not a small key, return the length we read */
334     *RealLength = Length;
335     return FALSE;
336 }
337 
338 //
339 // Returns whether or not this is a big valued key
340 //
341 static inline
342 BOOLEAN
343 CmpIsKeyValueBig(IN PHHIVE Hive,
344                  IN ULONG Length)
345 {
346     /* Check if the hive is XP Beta 1 or newer */
347     if (Hive->Version >= HSYS_WHISTLER_BETA1)
348     {
349         /* Check if the key length is valid for a big value key */
350         if ((Length < CM_KEY_VALUE_SPECIAL_SIZE) && (Length > CM_KEY_VALUE_BIG))
351         {
352             /* Yes, this value is big */
353             return TRUE;
354         }
355     }
356 
357     /* Not a big value key */
358     return FALSE;
359 }
360 
361 /*
362  * Public Hive functions.
363  */
364 NTSTATUS CMAPI
365 HvInitialize(
366     PHHIVE RegistryHive,
367     ULONG OperationType,
368     ULONG HiveFlags,
369     ULONG FileType,
370     PVOID HiveData OPTIONAL,
371     PALLOCATE_ROUTINE Allocate,
372     PFREE_ROUTINE Free,
373     PFILE_SET_SIZE_ROUTINE FileSetSize,
374     PFILE_WRITE_ROUTINE FileWrite,
375     PFILE_READ_ROUTINE FileRead,
376     PFILE_FLUSH_ROUTINE FileFlush,
377     ULONG Cluster OPTIONAL,
378     PCUNICODE_STRING FileName OPTIONAL);
379 
380 VOID CMAPI
381 HvFree(
382    PHHIVE RegistryHive);
383 
384 #define HvGetCell(Hive, Cell)   \
385     (Hive)->GetCellRoutine(Hive, Cell)
386 
387 #define HvReleaseCell(Hive, Cell)               \
388 do {                                            \
389     if ((Hive)->ReleaseCellRoutine)             \
390         (Hive)->ReleaseCellRoutine(Hive, Cell); \
391 } while(0)
392 
393 LONG CMAPI
394 HvGetCellSize(
395    PHHIVE RegistryHive,
396    PVOID Cell);
397 
398 HCELL_INDEX CMAPI
399 HvAllocateCell(
400    PHHIVE RegistryHive,
401    ULONG Size,
402    HSTORAGE_TYPE Storage,
403    IN HCELL_INDEX Vicinity);
404 
405 BOOLEAN CMAPI
406 HvIsCellAllocated(
407     IN PHHIVE RegistryHive,
408     IN HCELL_INDEX CellIndex
409 );
410 
411 HCELL_INDEX CMAPI
412 HvReallocateCell(
413    PHHIVE RegistryHive,
414    HCELL_INDEX CellOffset,
415    ULONG Size);
416 
417 VOID CMAPI
418 HvFreeCell(
419    PHHIVE RegistryHive,
420    HCELL_INDEX CellOffset);
421 
422 BOOLEAN CMAPI
423 HvMarkCellDirty(
424    PHHIVE RegistryHive,
425    HCELL_INDEX CellOffset,
426    BOOLEAN HoldingLock);
427 
428 BOOLEAN CMAPI
429 HvIsCellDirty(
430     IN PHHIVE Hive,
431     IN HCELL_INDEX Cell
432 );
433 
434 BOOLEAN
435 CMAPI
436 HvHiveWillShrink(
437     IN PHHIVE RegistryHive
438 );
439 
440 BOOLEAN CMAPI
441 HvSyncHive(
442    PHHIVE RegistryHive);
443 
444 BOOLEAN CMAPI
445 HvWriteHive(
446    PHHIVE RegistryHive);
447 
448 BOOLEAN
449 CMAPI
450 HvTrackCellRef(
451     IN OUT PHV_TRACK_CELL_REF CellRef,
452     IN PHHIVE Hive,
453     IN HCELL_INDEX Cell
454 );
455 
456 VOID
457 CMAPI
458 HvReleaseFreeCellRefArray(
459     IN OUT PHV_TRACK_CELL_REF CellRef
460 );
461 
462 /*
463  * Private functions.
464  */
465 
466 PCELL_DATA CMAPI
467 HvpGetCellData(
468     _In_ PHHIVE Hive,
469     _In_ HCELL_INDEX CellIndex);
470 
471 PHBIN CMAPI
472 HvpAddBin(
473    PHHIVE RegistryHive,
474    ULONG Size,
475    HSTORAGE_TYPE Storage);
476 
477 NTSTATUS CMAPI
478 HvpCreateHiveFreeCellList(
479    PHHIVE Hive);
480 
481 ULONG CMAPI
482 HvpHiveHeaderChecksum(
483    PHBASE_BLOCK HiveHeader);
484 
485 
486 /* Old-style Public "Cmlib" functions */
487 
488 BOOLEAN CMAPI
489 CmCreateRootNode(
490    PHHIVE Hive,
491    PCWSTR Name);
492 
493 VOID CMAPI
494 CmPrepareHive(
495    PHHIVE RegistryHive);
496 
497 
498 /* NT-style Public Cm functions */
499 
500 //
501 // Cell Index Routines
502 //
503 HCELL_INDEX
504 NTAPI
505 CmpFindSubKeyByName(
506     IN PHHIVE Hive,
507     IN PCM_KEY_NODE Parent,
508     IN PCUNICODE_STRING SearchName
509 );
510 
511 HCELL_INDEX
512 NTAPI
513 CmpFindSubKeyByNumber(
514     IN PHHIVE Hive,
515     IN PCM_KEY_NODE Node,
516     IN ULONG Number
517 );
518 
519 ULONG
520 NTAPI
521 CmpComputeHashKey(
522     IN ULONG Hash,
523     IN PCUNICODE_STRING Name,
524     IN BOOLEAN AllowSeparators
525 );
526 
527 BOOLEAN
528 NTAPI
529 CmpAddSubKey(
530     IN PHHIVE Hive,
531     IN HCELL_INDEX Parent,
532     IN HCELL_INDEX Child
533 );
534 
535 BOOLEAN
536 NTAPI
537 CmpRemoveSubKey(
538     IN PHHIVE Hive,
539     IN HCELL_INDEX ParentKey,
540     IN HCELL_INDEX TargetKey
541 );
542 
543 BOOLEAN
544 NTAPI
545 CmpMarkIndexDirty(
546     IN PHHIVE Hive,
547     HCELL_INDEX ParentKey,
548     HCELL_INDEX TargetKey
549 );
550 
551 
552 //
553 // Name Functions
554 //
555 LONG
556 NTAPI
557 CmpCompareCompressedName(
558     IN PCUNICODE_STRING SearchName,
559     IN PWCHAR CompressedName,
560     IN ULONG NameLength
561 );
562 
563 USHORT
564 NTAPI
565 CmpNameSize(
566     IN PHHIVE Hive,
567     IN PCUNICODE_STRING Name
568 );
569 
570 USHORT
571 NTAPI
572 CmpCompressedNameSize(
573     IN PWCHAR Name,
574     IN ULONG Length
575 );
576 
577 USHORT
578 NTAPI
579 CmpCopyName(
580     IN PHHIVE Hive,
581     OUT PWCHAR Destination,
582     IN PCUNICODE_STRING Source
583 );
584 
585 VOID
586 NTAPI
587 CmpCopyCompressedName(
588     OUT PWCHAR Destination,
589     IN ULONG DestinationLength,
590     IN PWCHAR Source,
591     IN ULONG SourceLength
592 );
593 
594 BOOLEAN
595 NTAPI
596 CmpFindNameInList(
597     IN PHHIVE Hive,
598     IN PCHILD_LIST ChildList,
599     IN PCUNICODE_STRING Name,
600     OUT PULONG ChildIndex OPTIONAL,
601     OUT PHCELL_INDEX CellIndex
602 );
603 
604 
605 //
606 // Cell Value Routines
607 //
608 HCELL_INDEX
609 NTAPI
610 CmpFindValueByName(
611     IN PHHIVE Hive,
612     IN PCM_KEY_NODE KeyNode,
613     IN PCUNICODE_STRING Name
614 );
615 
616 PCELL_DATA
617 NTAPI
618 CmpValueToData(
619     IN PHHIVE Hive,
620     IN PCM_KEY_VALUE Value,
621     OUT PULONG Length
622 );
623 
624 NTSTATUS
625 NTAPI
626 CmpSetValueDataNew(
627     IN PHHIVE Hive,
628     IN PVOID Data,
629     IN ULONG DataSize,
630     IN HSTORAGE_TYPE StorageType,
631     IN HCELL_INDEX ValueCell,
632     OUT PHCELL_INDEX DataCell
633 );
634 
635 NTSTATUS
636 NTAPI
637 CmpAddValueToList(
638     IN PHHIVE Hive,
639     IN HCELL_INDEX ValueCell,
640     IN ULONG Index,
641     IN HSTORAGE_TYPE StorageType,
642     IN OUT PCHILD_LIST ChildList
643 );
644 
645 BOOLEAN
646 NTAPI
647 CmpFreeValue(
648     IN PHHIVE Hive,
649     IN HCELL_INDEX Cell
650 );
651 
652 BOOLEAN
653 NTAPI
654 CmpMarkValueDataDirty(
655     IN PHHIVE Hive,
656     IN PCM_KEY_VALUE Value
657 );
658 
659 BOOLEAN
660 NTAPI
661 CmpFreeValueData(
662     IN PHHIVE Hive,
663     IN HCELL_INDEX DataCell,
664     IN ULONG DataLength
665 );
666 
667 NTSTATUS
668 NTAPI
669 CmpRemoveValueFromList(
670     IN PHHIVE Hive,
671     IN ULONG Index,
672     IN OUT PCHILD_LIST ChildList
673 );
674 
675 BOOLEAN
676 NTAPI
677 CmpGetValueData(
678     IN PHHIVE Hive,
679     IN PCM_KEY_VALUE Value,
680     OUT PULONG Length,
681     OUT PVOID *Buffer,
682     OUT PBOOLEAN BufferAllocated,
683     OUT PHCELL_INDEX CellToRelease
684 );
685 
686 NTSTATUS
687 NTAPI
688 CmpCopyKeyValueList(
689     IN PHHIVE SourceHive,
690     IN PCHILD_LIST SrcValueList,
691     IN PHHIVE DestinationHive,
692     IN OUT PCHILD_LIST DestValueList,
693     IN HSTORAGE_TYPE StorageType
694 );
695 
696 NTSTATUS
697 NTAPI
698 CmpFreeKeyByCell(
699     IN PHHIVE Hive,
700     IN HCELL_INDEX Cell,
701     IN BOOLEAN Unlink
702 );
703 
704 VOID
705 NTAPI
706 CmpRemoveSecurityCellList(
707     IN PHHIVE Hive,
708     IN HCELL_INDEX SecurityCell
709 );
710 
711 VOID
712 NTAPI
713 CmpFreeSecurityDescriptor(
714     IN PHHIVE Hive,
715     IN HCELL_INDEX Cell
716 );
717 
718 /******************************************************************************/
719 
720 /* To be implemented by the user of this library */
721 PVOID
722 NTAPI
723 CmpAllocate(
724     IN SIZE_T Size,
725     IN BOOLEAN Paged,
726     IN ULONG Tag
727 );
728 
729 VOID
730 NTAPI
731 CmpFree(
732     IN PVOID Ptr,
733     IN ULONG Quota
734 );
735 
736 #endif /* _CMLIB_H_ */
737