1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/installer/mini_installer/mini_string.h"
6
7#include <windows.h>
8
9namespace {
10
11// Returns true if the given two ASCII characters are same (ignoring case).
12bool EqualASCIICharI(wchar_t a, wchar_t b) {
13  if (a >= L'A' && a <= L'Z')
14    a += (L'a' - L'A');
15  if (b >= L'A' && b <= L'Z')
16    b += (L'a' - L'A');
17  return (a == b);
18}
19
20}  // namespace
21
22namespace mini_installer {
23
24// Formats a sequence of |bytes| as hex.  The |str| buffer must have room for
25// at least 2*|size| + 1.
26bool HexEncode(const void* bytes, size_t size, wchar_t* str, size_t str_size) {
27  if (str_size <= (size * 2))
28    return false;
29
30  static const wchar_t kHexChars[] = L"0123456789ABCDEF";
31
32  str[size * 2] = L'\0';
33
34  for (size_t i = 0; i < size; ++i) {
35    char b = reinterpret_cast<const char*>(bytes)[i];
36    str[(i * 2)] = kHexChars[(b >> 4) & 0xf];
37    str[(i * 2) + 1] = kHexChars[b & 0xf];
38  }
39
40  return true;
41}
42
43size_t SafeStrLen(const wchar_t* str, size_t alloc_size) {
44  if (!str || !alloc_size)
45    return 0;
46  size_t len = 0;
47  while (--alloc_size && str[len] != L'\0')
48    ++len;
49  return len;
50}
51
52bool SafeStrCopy(wchar_t* dest, size_t dest_size, const wchar_t* src) {
53  if (!dest || !dest_size)
54    return false;
55
56  wchar_t* write = dest;
57  for (size_t remaining = dest_size; remaining != 0; --remaining) {
58    if ((*write++ = *src++) == L'\0')
59      return true;
60  }
61
62  // If we fail, we do not want to leave the string with partially copied
63  // contents.  The reason for this is that we use these strings mostly for
64  // named objects such as files.  If we copy a partial name, then that could
65  // match with something we do not want it to match with.
66  // Furthermore, since SafeStrCopy is called from SafeStrCat, we do not
67  // want to mutate the string in case the caller handles the error of a
68  // failed concatenation.  For example:
69  //
70  // wchar_t buf[5] = {0};
71  // if (!SafeStrCat(buf, arraysize(buf), kLongName))
72  //   SafeStrCat(buf, arraysize(buf), kShortName);
73  //
74  // If we were to return false in the first call to SafeStrCat but still
75  // mutate the buffer, the buffer will be in an unexpected state.
76  *dest = L'\0';
77  return false;
78}
79
80// Safer replacement for lstrcat function.
81bool SafeStrCat(wchar_t* dest, size_t dest_size, const wchar_t* src) {
82  // Use SafeStrLen instead of lstrlen just in case the |dest| buffer isn't
83  // terminated.
84  int str_len = SafeStrLen(dest, dest_size);
85  return SafeStrCopy(dest + str_len, dest_size - str_len, src);
86}
87
88bool StrEndsWith(const wchar_t* str, const wchar_t* end_str) {
89  if (str == NULL || end_str == NULL)
90    return false;
91
92  for (int i = lstrlen(str) - 1, j = lstrlen(end_str) - 1; j >= 0; --i, --j) {
93    if (i < 0 || !EqualASCIICharI(str[i], end_str[j]))
94      return false;
95  }
96
97  return true;
98}
99
100bool StrStartsWith(const wchar_t* str, const wchar_t* start_str) {
101  if (str == NULL || start_str == NULL)
102    return false;
103
104  for (int i = 0; start_str[i] != L'\0'; ++i) {
105    if (!EqualASCIICharI(str[i], start_str[i]))
106      return false;
107  }
108
109  return true;
110}
111
112const wchar_t* SearchStringI(const wchar_t* source, const wchar_t* find) {
113  if (!find || find[0] == L'\0')
114    return source;
115
116  const wchar_t* scan = source;
117  while (*scan) {
118    const wchar_t* s = scan;
119    const wchar_t* f = find;
120
121    while (*s && *f && EqualASCIICharI(*s, *f))
122      ++s, ++f;
123
124    if (!*f)
125      return scan;
126
127    ++scan;
128  }
129
130  return NULL;
131}
132
133bool FindTagInStr(const wchar_t* str,
134                  const wchar_t* tag,
135                  const wchar_t** position) {
136  int tag_length = ::lstrlen(tag);
137  const wchar_t* scan = str;
138  for (const wchar_t* tag_start = SearchStringI(scan, tag);
139       tag_start != NULL;
140       tag_start = SearchStringI(scan, tag)) {
141    scan = tag_start + tag_length;
142    if (*scan == L'-' || *scan == L'\0') {
143      if (position != NULL)
144        *position = tag_start;
145      return true;
146    }
147  }
148  return false;
149}
150
151wchar_t* GetNameFromPathExt(wchar_t* path, size_t size) {
152  if (size <= 1)
153    return NULL;
154
155  wchar_t* current = &path[size - 1];
156  while (current != path && L'\\' != *current)
157    --current;
158
159  return (current == path) ? NULL : (current + 1);
160}
161
162}  // namespace mini_installer
163